PluginProbe ʕ •ᴥ•ʔ
WPForms – Easy Form Builder for WordPress – Contact Forms, Payment Forms, Surveys, & More / 1.4.9
WPForms – Easy Form Builder for WordPress – Contact Forms, Payment Forms, Surveys, & More v1.4.9
1.10.1 1.10.0.5 trunk 1.1.4 1.1.4.2 1.1.5 1.1.5.1 1.1.6 1.1.6.1 1.1.7 1.1.7.1 1.1.7.2 1.1.8 1.1.8.1 1.1.8.2 1.1.8.3 1.1.8.4 1.10.0.1 1.10.0.2 1.10.0.3 1.10.0.4 1.2.0 1.2.0.1 1.2.1 1.2.2 1.2.2.1 1.2.2.2 1.2.3 1.2.3.1 1.2.3.2 1.2.4 1.2.4.1 1.2.5 1.2.5.1 1.2.6 1.2.7 1.2.8 1.2.8.1 1.2.9 1.3.0 1.3.1 1.3.1.1 1.3.1.2 1.3.2 1.3.3 1.3.5 1.3.6 1.3.6.1 1.3.6.2 1.3.7.2 1.3.7.3 1.3.7.4 1.3.8 1.3.9.1 1.4.0.1 1.4.1.1 1.4.2 1.4.2.1 1.4.2.2 1.4.3 1.4.4 1.4.4.1 1.4.5 1.4.5.1 1.4.5.2 1.4.5.3 1.4.6 1.4.7.1 1.4.7.2 1.4.8.1 1.4.9 1.5.0.1 1.5.0.3 1.5.0.4 1.5.1 1.5.1.1 1.5.1.3 1.5.2.1 1.5.2.2 1.5.2.3 1.5.3 1.5.3.1 1.5.4.1 1.5.4.2 1.5.5 1.5.5.1 1.5.6 1.5.6.2 1.5.7 1.5.8.2 1.5.9.1 1.5.9.4 1.5.9.5 1.6.0.1 1.6.0.2 1.6.1 1.6.2.2 1.6.2.3 1.6.3.1 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.7 1.6.7.1 1.6.7.2 1.6.7.3 1.6.8 1.6.8.1 1.6.9 1.7.0 1.7.1.1 1.7.1.2 1.7.2 1.7.2.1 1.7.3 1.7.4 1.7.4.1 1.7.4.2 1.7.5.1 1.7.5.2 1.7.5.3 1.7.5.5 1.7.6 1.7.7 1.7.7.1 1.7.7.2 1.7.8 1.7.9 1.7.9.1 1.8.0.1 1.8.0.2 1.8.1.1 1.8.1.2 1.8.1.3 1.8.2.1 1.8.2.2 1.8.2.3 1.8.3 1.8.3.1 1.8.4 1.8.4.1 1.8.5.2 1.8.5.3 1.8.5.4 1.8.6.2 1.8.6.3 1.8.6.4 1.8.7.2 1.8.8.2 1.8.8.3 1.8.9.1 1.8.9.2 1.8.9.4 1.8.9.5 1.8.9.6 1.9.0.1 1.9.0.2 1.9.0.3 1.9.0.4 1.9.1.1 1.9.1.2 1.9.1.3 1.9.1.4 1.9.1.5 1.9.1.6 1.9.2.1 1.9.2.2 1.9.2.3 1.9.3.1 1.9.3.2 1.9.4.1 1.9.4.2 1.9.5 1.9.5.1 1.9.5.2 1.9.6 1.9.6.1 1.9.6.2 1.9.7.1 1.9.7.2 1.9.7.3 1.9.8.1 1.9.8.2 1.9.8.4 1.9.8.7 1.9.9.2 1.9.9.3 1.9.9.4
wpforms-lite / includes / class-process.php
wpforms-lite / includes Last commit date
admin 7 years ago analytics 7 years ago emails 7 years ago fields 7 years ago providers 7 years ago templates 7 years ago class-conditional-logic-core.php 7 years ago class-fields.php 7 years ago class-form.php 7 years ago class-frontend.php 7 years ago class-install.php 9 years ago class-logging.php 7 years ago class-preview.php 7 years ago class-process.php 7 years ago class-providers.php 8 years ago class-smart-tags.php 7 years ago class-templates.php 8 years ago class-widget.php 7 years ago functions.php 7 years ago integrations.php 7 years ago
class-process.php
487 lines
1 <?php
2 /**
3 * Process and validate form entries.
4 *
5 * @package WPForms
6 * @author WPForms
7 * @since 1.0.0
8 * @license GPL-2.0+
9 * @copyright Copyright (c) 2016, WPForms LLC
10 */
11 class WPForms_Process {
12
13 /**
14 * Holds errors.
15 *
16 * @since 1.0.0
17 *
18 * @var array
19 */
20 public $errors;
21
22 /**
23 * Holds formatted fields.
24 *
25 * @since 1.0.0
26 *
27 * @var array
28 */
29 public $fields;
30
31 /**
32 * Holds the ID of a successful entry.
33 *
34 * @since 1.2.3
35 *
36 * @var int
37 */
38 public $entry_id = 0;
39
40 /**
41 * Holds form data.
42 *
43 * @since 1.4.5
44 *
45 * @var array
46 */
47 public $form_data;
48
49 /**
50 * If a valid return has was processed.
51 *
52 * @since 1.4.5
53 *
54 * @var bool
55 */
56 public $valid_hash = false;
57
58 /**
59 * Primary class constructor.
60 *
61 * @since 1.0.0
62 */
63 public function __construct() {
64
65 add_action( 'wp', array( $this, 'listen' ) );
66 }
67
68 /**
69 * Listen to see if this is a return callback or a posted form entry.
70 *
71 * @since 1.0.0
72 */
73 public function listen() {
74
75 if ( ! empty( $_GET['wpforms_return'] ) ) {
76 $this->entry_confirmation_redirect( '', $_GET['wpforms_return'] );
77 }
78
79 if ( ! empty( $_POST['wpforms']['id'] ) ) {
80 $this->process( stripslashes_deep( $_POST['wpforms'] ) );
81 }
82 }
83
84 /**
85 * Process the form entry.
86 *
87 * @since 1.0.0
88 *
89 * @param array $entry $_POST object.
90 */
91 public function process( $entry ) {
92
93 $this->errors = array();
94 $this->fields = array();
95 $form_id = absint( $entry['id'] );
96 $form = wpforms()->form->get( $form_id );
97 $honeypot = false;
98
99 // Validate form is real and active (published).
100 if ( ! $form || 'publish' !== $form->post_status ) {
101 $this->errors[ $form_id ]['header'] = esc_html__( 'Invalid form.', 'wpforms' );
102 return;
103 }
104
105 // Formatted form data for hooks.
106 $form_data = apply_filters( 'wpforms_process_before_form_data', wpforms_decode( $form->post_content ), $entry );
107
108 // Pre-process/validate hooks and filter. Data is not validated or
109 // cleaned yet so use with caution.
110 $entry = apply_filters( 'wpforms_process_before_filter', $entry, $form_data );
111
112 do_action( 'wpforms_process_before', $entry, $form_data );
113 do_action( "wpforms_process_before_{$form_id}", $entry, $form_data );
114
115 // Validate fields.
116 foreach ( $form_data['fields'] as $field ) {
117
118 $field_id = $field['id'];
119 $field_type = $field['type'];
120 $field_submit = isset( $entry['fields'][ $field_id ] ) ? $entry['fields'][ $field_id ] : '';
121
122 do_action( "wpforms_process_validate_{$field_type}", $field_id, $field_submit, $form_data );
123 }
124
125 // reCAPTCHA check.
126 $site_key = wpforms_setting( 'recaptcha-site-key', '' );
127 $secret_key = wpforms_setting( 'recaptcha-secret-key', '' );
128 if (
129 ! empty( $site_key ) &&
130 ! empty( $secret_key ) &&
131 isset( $form_data['settings']['recaptcha'] ) &&
132 '1' == $form_data['settings']['recaptcha']
133 ) {
134 if ( ! empty( $_POST['g-recaptcha-response'] ) ) {
135 $data = wp_remote_get( 'https://www.google.com/recaptcha/api/siteverify?secret=' . $secret_key . '&response=' . $_POST['g-recaptcha-response'] );
136 $data = json_decode( wp_remote_retrieve_body( $data ) );
137 if ( empty( $data->success ) ) {
138 $this->errors[ $form_id ]['recaptcha'] = esc_html__( 'Incorrect reCAPTCHA, please try again.', 'wpforms' );
139 }
140 } else {
141 $this->errors[ $form_id ]['recaptcha'] = esc_html__( 'reCAPTCHA is required.', 'wpforms' );
142 }
143 }
144
145 // Initial error check.
146 // Don't proceed if there are any errors thus far. We provide a filter
147 // so that other features, such as conditional logic, have the ability
148 // to adjust blocking errors.
149 $errors = apply_filters( 'wpforms_process_initial_errors', $this->errors, $form_data );
150
151 if ( ! empty( $errors[ $form_id ] ) ) {
152 if ( empty( $this->errors[ $form_id ]['header'] ) ) {
153 $errors[ $form_id ]['header'] = esc_html__( 'Form has not been submitted, please see the errors below.', 'wpforms' );
154 }
155 $this->errors = $errors;
156 return;
157 }
158
159 // Validate honeypot early - before actual processing.
160 if (
161 ! empty( $form_data['settings']['honeypot'] ) &&
162 '1' == $form_data['settings']['honeypot'] &&
163 ! empty( $entry['hp'] )
164 ) {
165 $honeypot = esc_html__( 'WPForms honeypot field triggered.', 'wpforms' );
166 }
167
168 $honeypot = apply_filters( 'wpforms_process_honeypot', $honeypot, $this->fields, $entry, $form_data );
169
170 // If spam - return early.
171 if ( $honeypot ) {
172 // Logs spam entry depending on log levels set.
173 wpforms_log(
174 'Spam Entry ' . uniqid(),
175 array( $honeypot, $entry ),
176 array(
177 'type' => array( 'spam' ),
178 'form_id' => $form_data['id'],
179 )
180 );
181
182 return;
183 }
184
185 // Pass the form created date into the form data.
186 $form_data['created'] = $form->post_date;
187
188 // Format fields.
189 foreach ( (array) $form_data['fields'] as $field ) {
190
191 $field_id = $field['id'];
192 $field_type = $field['type'];
193 $field_submit = isset( $entry['fields'][ $field_id ] ) ? $entry['fields'][ $field_id ] : '';
194
195 do_action( "wpforms_process_format_{$field_type}", $field_id, $field_submit, $form_data );
196 }
197
198 // This hook is for internal purposes and should not be leveraged.
199 do_action( 'wpforms_process_format_after', $form_data );
200
201 // Process hooks/filter - this is where most addons should hook
202 // because at this point we have completed all field validation and
203 // formatted the data.
204 $this->fields = apply_filters( 'wpforms_process_filter', $this->fields, $entry, $form_data );
205
206 do_action( 'wpforms_process', $this->fields, $entry, $form_data );
207 do_action( "wpforms_process_{$form_id}", $this->fields, $entry, $form_data );
208
209 $this->fields = apply_filters( 'wpforms_process_after_filter', $this->fields, $entry, $form_data );
210
211 // One last error check - don't proceed if there are any errors.
212 if ( ! empty( $this->errors[ $form_id ] ) ) {
213 if ( empty( $this->errors[ $form_id ]['header'] ) ) {
214 $this->errors[ $form_id ]['header'] = esc_html__( 'Form has not been submitted, please see the errors below.', 'wpforms' );
215 }
216 return;
217 }
218
219 // Success - add entry to database.
220 $entry_id = $this->entry_save( $this->fields, $entry, $form_data['id'], $form_data );
221
222 // Success - send email notification.
223 $this->entry_email( $this->fields, $entry, $form_data, $entry_id, 'entry' );
224
225 // Pass completed and formatted fields in POST.
226 $_POST['wpforms']['complete'] = $this->fields;
227
228 // Pass entry ID in POST.
229 $_POST['wpforms']['entry_id'] = $entry_id;
230
231 // Logs entry depending on log levels set.
232 wpforms_log(
233 $entry_id ? "Entry {$entry_id}" : 'Entry',
234 $this->fields,
235 array(
236 'type' => array( 'entry' ),
237 'parent' => $entry_id,
238 'form_id' => $form_data['id'],
239 )
240 );
241
242 // Post-process hooks.
243 do_action( 'wpforms_process_complete', $this->fields, $entry, $form_data, $entry_id );
244 do_action( "wpforms_process_complete_{$form_id}", $this->fields, $entry, $form_data, $entry_id );
245
246 $this->entry_confirmation_redirect( $form_data );
247 }
248
249 /**
250 * Validate the form return hash.
251 *
252 * @since 1.0.0
253 *
254 * @param string $hash
255 * @return mixed false for invalid or form id
256 */
257 public function validate_return_hash( $hash = '' ) {
258
259 $query_args = base64_decode( $hash );
260
261 parse_str( $query_args, $output );
262
263 // Verify hash matches.
264 if ( wp_hash( $output['form_id'] . ',' . $output['entry_id'] ) !== $output['hash'] ) {
265 return false;
266 }
267
268 // Get lead and verify it is attached to the form we received with it.
269 $entry = wpforms()->entry->get( $output['entry_id'] );
270
271 if ( $output['form_id'] != $entry->form_id ) {
272 return false;
273 }
274
275 return array(
276 'form_id' => absint( $output['form_id'] ),
277 'entry_id' => absint( $output['form_id'] ),
278 'fields' => $entry->fields,
279 );
280 }
281
282 /**
283 * Redirects user to a page or URL specified in the form confirmation settings.
284 *
285 * @since 1.0.0
286 *
287 * @param array|string $form_data
288 * @param string $hash
289 */
290 public function entry_confirmation_redirect( $form_data = array(), $hash = '' ) {
291
292 // Maybe process return hash.
293 if ( ! empty( $hash ) ) {
294
295 $hash_data = $this->validate_return_hash( $hash );
296
297 if ( ! $hash_data || ! is_array( $hash_data ) ) {
298 return;
299 }
300
301 $this->valid_hash = true;
302 $this->entry_id = absint( $hash_data['entry_id'] );
303 $this->fields = json_decode( $hash_data['fields'], true );
304 $this->form_data = wpforms()->form->get( absint( $hash_data['form_id'] ), array(
305 'content_only' => true,
306 ) );
307
308 } else {
309
310 $this->form_data = $form_data;
311 }
312
313 // Backward compatibility.
314 if ( empty( $this->form_data['settings']['confirmations'] ) ) {
315 $this->form_data['settings']['confirmations'][1]['type'] = ! empty( $this->form_data['settings']['confirmation_type'] ) ? $this->form_data['settings']['confirmation_type'] : 'message';
316 $this->form_data['settings']['confirmations'][1]['message'] = ! empty( $this->form_data['settings']['confirmation_message'] ) ? $this->form_data['settings']['confirmation_message'] : esc_html__( 'Thanks for contacting us! We will be in touch with you shortly.', 'wpforms' );
317 $this->form_data['settings']['confirmations'][1]['message_scroll'] = ! empty( $this->form_data['settings']['confirmation_message_scroll'] ) ? $this->form_data['settings']['confirmation_message_scroll'] : 1;
318 $this->form_data['settings']['confirmations'][1]['page'] = ! empty( $this->form_data['settings']['confirmation_page'] ) ? $this->form_data['settings']['confirmation_page'] : '';
319 $this->form_data['settings']['confirmations'][1]['redirect'] = ! empty( $this->form_data['settings']['confirmation_redirect'] ) ? $this->form_data['settings']['confirmation_redirect'] : '';
320 }
321
322 if ( empty( $this->form_data['settings']['confirmations'] ) || ! is_array( $this->form_data['settings']['confirmations'] ) ) {
323 return;
324 }
325
326 $confirmations = $this->form_data['settings']['confirmations'];
327
328 // Reverse sort confirmations by id to process newer ones first.
329 krsort( $confirmations );
330
331 $default_confirmation_key = min( array_keys( $confirmations ) );
332
333 foreach ( $confirmations as $confirmation_id => $confirmation ) {
334 // Last confirmation should execute in any case.
335 if ( $default_confirmation_key === $confirmation_id ) {
336 break;
337 }
338 $process_confirmation = apply_filters( 'wpforms_entry_confirmation_process', true, $this->fields, $form_data, $confirmation_id );
339 if ( $process_confirmation ) {
340 break;
341 }
342 }
343
344 $url = '';
345 // Redirect if needed, to either a page or URL, after form processing.
346 if ( ! empty( $confirmations[ $confirmation_id ]['type'] ) && 'message' !== $confirmations[ $confirmation_id ]['type'] ) {
347
348 if ( 'redirect' === $confirmations[ $confirmation_id ]['type'] ) {
349 $url = apply_filters( 'wpforms_process_smart_tags', $confirmations[ $confirmation_id ]['redirect'], $this->form_data, $this->fields, $this->entry_id );
350 }
351
352 if ( 'page' === $confirmations[ $confirmation_id ]['type'] ) {
353 $url = get_permalink( (int) $confirmations[ $confirmation_id ]['page'] );
354 }
355 }
356
357 if ( ! empty( $url ) ) {
358 $url = apply_filters( 'wpforms_process_redirect_url', $url, $this->form_data['id'], $this->fields, $this->form_data, $this->entry_id );
359 wp_redirect( esc_url_raw( $url ) );
360 do_action( 'wpforms_process_redirect', $this->form_data['id'] );
361 do_action( "wpforms_process_redirect_{$this->form_data['id']}", $this->form_data['id'] );
362 exit;
363 }
364
365 // Pass a message to a frontend if no redirection happened.
366 if ( ! empty( $confirmations[ $confirmation_id ]['type'] ) && 'message' === $confirmations[ $confirmation_id ]['type'] ) {
367 wpforms()->frontend->confirmation_message = $confirmations[ $confirmation_id ]['message'];
368
369 if ( ! empty( $confirmations[ $confirmation_id ]['message_scroll'] ) ) {
370 wpforms()->frontend->confirmation_message_scroll = true;
371 }
372 }
373 }
374
375 /**
376 * Sends entry email notifications.
377 *
378 * @since 1.0.0
379 *
380 * @param array $fields
381 * @param array $entry
382 * @param array $form_data
383 * @param int $entry_id
384 * @param string $context
385 */
386 public function entry_email( $fields, $entry, $form_data, $entry_id, $context = '' ) {
387
388 // Check that the form was configured for email notifications.
389 if (
390 empty( $form_data['settings']['notification_enable'] ) ||
391 '1' != $form_data['settings']['notification_enable']
392 ) {
393 return;
394 }
395
396 // Provide the opportunity to override via a filter.
397 if ( ! apply_filters( 'wpforms_entry_email', true, $fields, $entry, $form_data ) ) {
398 return;
399 }
400
401 // Make sure we have and entry id.
402 if ( empty( $this->entry_id ) ) {
403 $this->entry_id = (int) $entry_id;
404 }
405
406 $fields = apply_filters( 'wpforms_entry_email_data', $fields, $entry, $form_data );
407
408 // Backwards compatibility for notifications before v1.2.3.
409 if ( empty( $form_data['settings']['notifications'] ) ) {
410 $notifications[1] = array(
411 'email' => $form_data['settings']['notification_email'],
412 'subject' => $form_data['settings']['notification_subject'],
413 'sender_name' => $form_data['settings']['notification_fromname'],
414 'sender_address' => $form_data['settings']['notification_fromaddress'],
415 'replyto' => $form_data['settings']['notification_replyto'],
416 'message' => '{all_fields}',
417 );
418 } else {
419 $notifications = $form_data['settings']['notifications'];
420 }
421
422 foreach ( $notifications as $notification_id => $notification ) {
423
424 if ( empty( $notification['email'] ) ) {
425 continue;
426 }
427
428 $process_email = apply_filters( 'wpforms_entry_email_process', true, $fields, $form_data, $notification_id, $context );
429
430 if ( ! $process_email ) {
431 continue;
432 }
433
434 $email = array();
435
436 // Setup email properties.
437 /* translators: %s - form name. */
438 $email['subject'] = ! empty( $notification['subject'] ) ? $notification['subject'] : sprintf( esc_html__( 'New %s Entry', 'wpforms' ), $form_data['settings']['form_title'] );
439 $email['address'] = explode( ',', apply_filters( 'wpforms_process_smart_tags', $notification['email'], $form_data, $fields, $this->entry_id ) );
440 $email['address'] = array_map( 'sanitize_email', $email['address'] );
441 $email['sender_address'] = ! empty( $notification['sender_address'] ) ? $notification['sender_address'] : get_option( 'admin_email' );
442 $email['sender_name'] = ! empty( $notification['sender_name'] ) ? $notification['sender_name'] : get_bloginfo( 'name' );
443 $email['replyto'] = ! empty( $notification['replyto'] ) ? $notification['replyto'] : false;
444 $email['message'] = ! empty( $notification['message'] ) ? $notification['message'] : '{all_fields}';
445 $email = apply_filters( 'wpforms_entry_email_atts', $email, $fields, $entry, $form_data, $notification_id );
446
447 // Create new email.
448 $emails = new WPForms_WP_Emails();
449 $emails->__set( 'form_data', $form_data );
450 $emails->__set( 'fields', $fields );
451 $emails->__set( 'entry_id', $this->entry_id );
452 $emails->__set( 'from_name', $email['sender_name'] );
453 $emails->__set( 'from_address', $email['sender_address'] );
454 $emails->__set( 'reply_to', $email['replyto'] );
455
456 // Maybe include CC.
457 if ( ! empty( $notification['carboncopy'] ) && wpforms_setting( 'email-carbon-copy', false ) ) {
458 $emails->__set( 'cc', $notification['carboncopy'] );
459 }
460
461 // Go.
462 foreach ( $email['address'] as $address ) {
463 $emails->send( trim( $address ), $email['subject'], $email['message'] );
464 }
465 } // End foreach().
466 }
467
468 /**
469 * Saves entry to database.
470 *
471 * @since 1.0.0
472 *
473 * @param array $fields
474 * @param array $entry
475 * @param int $form_id
476 * @param array|string $form_data
477 *
478 * @return int
479 */
480 public function entry_save( $fields, $entry, $form_id, $form_data = '' ) {
481
482 do_action( 'wpforms_process_entry_save', $fields, $entry, $form_id, $form_data );
483
484 return $this->entry_id;
485 }
486 }
487