PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.27
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.27
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / helpers / FrmEntriesHelper.php

FrmEntriesHelper.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.27, at classes/helpers/FrmEntriesHelper.php

978 lines 29.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmEntriesHelper {
7
8 /**
9 * "Submitted" entry status.
10 *
11 * @since 6.4.2
12 *
13 * @var int
14 */
15 const SUBMITTED_ENTRY_STATUS = 0;
16
17 /**
18 * "Draft" entry status.
19 *
20 * @since 6.4.2
21 *
22 * @var int
23 */
24 const DRAFT_ENTRY_STATUS = 1;
25
26 /**
27 * @param mixed $fields
28 * @param object|string $form
29 * @param bool $reset
30 * @param array $args
31 *
32 * @return array
33 */
34 public static function setup_new_vars( $fields, $form = '', $reset = false, $args = array() ) {
35 remove_action( 'media_buttons', 'FrmFormsController::insert_form_button' );
36
37 $values = array(
38 'name' => '',
39 'description' => '',
40 'item_key' => '',
41 );
42
43 $values['fields'] = array();
44
45 if ( ! $fields ) {
46 return apply_filters( 'frm_setup_new_entry', $values );
47 }
48
49 foreach ( (array) $fields as $field ) {
50 $original_default = $field->default_value;
51 self::prepare_field_default_value( $field );
52 $new_value = self::get_field_value_for_new_entry( $field, $reset, $args );
53 $field_array = FrmAppHelper::start_field_array( $field );
54 $field_array['value'] = $new_value;
55 $field_array['type'] = apply_filters( 'frm_field_type', $field->type, $field, $new_value );
56 $field_array['parent_form_id'] = $args['parent_form_id'] ?? $field->form_id;
57 $field_array['reset_value'] = $reset;
58 $field_array['in_embed_form'] = $args['in_embed_form'] ?? '0';
59 $field_array['original_default'] = $original_default;
60
61 FrmFieldsHelper::prepare_new_front_field( $field_array, $field, $args );
62
63 if ( ! is_array( $field->field_options ) ) {
64 $field->field_options = array();
65 }
66
67 $field_array = array_merge( $field->field_options, $field_array );
68
69 $values['fields'][] = $field_array;
70
71 if ( ! $form || ! isset( $form->id ) ) {
72 $form = FrmForm::getOne( $field->form_id );
73 }
74 }//end foreach
75
76 FrmAppHelper::unserialize_or_decode( $form->options );
77
78 if ( is_array( $form->options ) ) {
79 $values = array_merge( $values, $form->options );
80 }
81
82 $form_defaults = FrmFormsHelper::get_default_opts();
83 $form_defaults['custom_style'] = FrmAppHelper::custom_style_value( array() );
84
85 $values = array_merge( $form_defaults, $values );
86
87 return apply_filters( 'frm_setup_new_entry', $values );
88 }
89
90 /**
91 * @since 2.05
92 *
93 * @param object $field
94 *
95 * @return void
96 */
97 private static function prepare_field_default_value( &$field ) {
98 // If checkbox, multi-select dropdown, or checkbox data from entries field, the value should be an array.
99 $return_array = FrmField::is_field_with_multiple_values( $field );
100
101 /**
102 * Do any shortcodes in default value and allow customization of default value.
103 * Calls FrmProFieldsHelper::get_default_value
104 */
105 $field->default_value = apply_filters( 'frm_get_default_value', $field->default_value, $field, true, $return_array );
106
107 if ( isset( $field->field_options['placeholder'] ) ) {
108 $field->field_options['placeholder'] = apply_filters( 'frm_get_default_value', $field->field_options['placeholder'], $field, false, false );
109 }
110 }
111
112 /**
113 * Set the value for each field
114 * This function is used when the form is first loaded and on all page turns *for a new entry*
115 *
116 * @since 2.0.13
117 *
118 * @param object $field - this is passed by reference since it is an object.
119 * @param bool $reset
120 * @param array $args
121 *
122 * @return array|string $new_value
123 */
124 private static function get_field_value_for_new_entry( $field, $reset, $args ) {
125 $new_value = $field->default_value;
126
127 if ( ! $reset && self::value_is_posted( $field, $args ) ) {
128 self::get_posted_value( $field, $new_value, $args );
129 }
130
131 if ( ! is_array( $new_value ) ) {
132 $new_value = str_replace( '"', '&quot;', $new_value );
133 }
134
135 return $new_value;
136 }
137
138 /**
139 * Check if a field has a posted value
140 *
141 * @since 2.01.0
142 *
143 * @param object $field
144 * @param array $args
145 *
146 * @return bool $value_is_posted
147 */
148 public static function value_is_posted( $field, $args ) {
149 $value_is_posted = false;
150
151 if ( $_POST ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
152 $repeating = ! empty( $args['repeating'] );
153
154 if ( $repeating ) {
155 if ( isset( $_POST['item_meta'][ $args['parent_field_id'] ][ $args['key_pointer'] ][ $field->id ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
156 $value_is_posted = true;
157 }
158 } elseif ( isset( $_POST['item_meta'][ $field->id ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
159 $value_is_posted = true;
160 }
161 }
162
163 return $value_is_posted;
164 }
165
166 /**
167 * @param array $values
168 * @param object $record
169 *
170 * @return array
171 */
172 public static function setup_edit_vars( $values, $record ) {
173 remove_action( 'media_buttons', 'FrmFormsController::insert_form_button' );
174
175 $values['item_key'] = FrmAppHelper::get_post_param( 'item_key', $record->item_key, 'sanitize_title' );
176 $values['form_id'] = $record->form_id;
177 $values['is_draft'] = $record->is_draft;
178
179 return apply_filters( 'frm_setup_edit_entry_vars', $values, $record );
180 }
181
182 /**
183 * @param string $message
184 * @param array $atts
185 *
186 * @return string
187 */
188 public static function replace_default_message( $message, $atts ) {
189 if ( ! str_contains( $message, '[default-message' ) &&
190 ! str_contains( $message, '[default_message' ) &&
191 $message ) {
192 return $message;
193 }
194
195 if ( ! $message ) {
196 $message = '[default-message]';
197 }
198
199 preg_match_all( "/\[(default-message|default_message)\b(.*?)(?:(\/))?\]/s", $message, $shortcodes, PREG_PATTERN_ORDER );
200
201 foreach ( $shortcodes[0] as $short_key => $tag ) {
202 $add_atts = FrmShortcodeHelper::get_shortcode_attribute_array( $shortcodes[2][ $short_key ] );
203 $this_atts = ! empty( $add_atts ) ? array_merge( $atts, $add_atts ) : $atts;
204 $default = FrmEntriesController::show_entry_shortcode( $this_atts );
205
206 // Add the default message.
207 $message = str_replace( $shortcodes[0][ $short_key ], $default, $message );
208 }
209
210 return $message;
211 }
212
213 /**
214 * @param stdClass $entry
215 * @param stdClass $field
216 * @param array $atts
217 *
218 * @return string
219 */
220 public static function prepare_display_value( $entry, $field, $atts ) {
221 $field_value = $entry->metas[ $field->id ] ?? false;
222
223 if ( FrmAppHelper::pro_is_installed() ) {
224 $empty = empty( $field_value );
225 FrmProEntriesHelper::get_dynamic_list_values( $field, $entry, $field_value );
226
227 if ( $empty && ! empty( $field_value ) ) {
228 // We've got an entry id, so switch it to a value.
229 $atts['force_id'] = true;
230 }
231 }
232
233 // phpcs:ignore Universal.Operators.StrictComparisons
234 if ( $field->form_id == $entry->form_id || empty( $atts['embedded_field_id'] ) ) {
235 return self::display_value( $field_value, $field, $atts );
236 }
237
238 if ( ! FrmAppHelper::pro_is_installed() ) {
239 return '';
240 }
241
242 if ( is_callable( 'FrmProEntriesHelper::prepare_child_display_value' ) ) {
243 return FrmProEntriesHelper::prepare_child_display_value( $entry, $field, $atts );
244 }
245
246 // This is an embedded form.
247 if ( str_starts_with( $atts['embedded_field_id'], 'form' ) ) {
248 // This is a repeating section.
249 $child_entries = FrmEntry::getAll( array( 'it.parent_item_id' => $entry->id ), '', '', true );
250 } else {
251 // Get all values for this field.
252 $child_values = $entry->metas[ $atts['embedded_field_id'] ] ?? false;
253
254 if ( $child_values ) {
255 $child_entries = FrmEntry::getAll( array( 'it.id' => (array) $child_values ) );
256 }
257 }
258
259 $field_value = array();
260
261 if ( empty( $child_entries ) ) {
262 return '';
263 }
264
265 foreach ( $child_entries as $child_entry ) {
266 $atts['item_id'] = $child_entry->id;
267 $atts['post_id'] = $child_entry->post_id;
268
269 // Fet the value for this field -- check for post values as well.
270 $entry_val = FrmProEntryMetaHelper::get_post_or_meta_value( $child_entry, $field );
271
272 if ( $entry_val || '0' === $entry_val ) {
273 // foreach entry get display_value.
274 $field_value[] = self::display_value( $entry_val, $field, $atts );
275 }
276
277 unset( $child_entry );
278 }
279
280 $sep = ', ';
281
282 if ( str_contains( implode( ' ', $field_value ), '<img' ) ) {
283 $sep = '<br/>';
284 }
285
286 $val = implode( $sep, $field_value );
287
288 return FrmAppHelper::kses( $val, 'all' );
289 }
290
291 /**
292 * Prepare the saved value for display
293 *
294 * @param array|string $value
295 * @param object $field
296 * @param array $atts
297 *
298 * @return string
299 */
300 public static function display_value( $value, $field, $atts = array() ) {
301 $defaults = array(
302 'type' => '',
303 'html' => false,
304 'show_filename' => true,
305 'truncate' => false,
306 'sep' => ', ',
307 'post_id' => 0,
308 'form_id' => $field->form_id,
309 'field' => $field,
310 'keepjs' => 0,
311 'return_array' => false,
312 );
313
314 $atts = wp_parse_args( $atts, $defaults );
315
316 if ( FrmField::is_image( $field ) || $field->type === 'star' ) {
317 $atts['truncate'] = false;
318 $atts['html'] = true;
319 }
320
321 $atts = apply_filters( 'frm_display_value_atts', $atts, $field, $value );
322
323 if ( is_string( $field->field_options ) ) {
324 $field->field_options = array();
325 }
326
327 if ( ! isset( $field->field_options['post_field'] ) ) {
328 $field->field_options['post_field'] = '';
329 }
330
331 if ( ! isset( $field->field_options['custom_field'] ) ) {
332 $field->field_options['custom_field'] = '';
333 }
334
335 if ( FrmAppHelper::pro_is_installed() && $atts['post_id'] && ( $field->field_options['post_field'] || $atts['type'] === 'tag' ) ) {
336 $atts['pre_truncate'] = $atts['truncate'];
337 $atts['truncate'] = true;
338 $atts['exclude_cat'] = $field->field_options['exclude_cat'] ?? 0;
339
340 $value = FrmProEntryMetaHelper::get_post_value( $atts['post_id'], $field->field_options['post_field'], $field->field_options['custom_field'], $atts );
341 $atts['truncate'] = $atts['pre_truncate'];
342 }
343
344 // phpcs:ignore Universal.Operators.StrictComparisons
345 if ( $value == '' ) {
346 return $value;
347 }
348
349 $unfiltered_value = $value;
350 FrmFieldsHelper::prepare_field_value( $unfiltered_value, $field->type );
351
352 $value = apply_filters( 'frm_display_value_custom', $unfiltered_value, $field, $atts );
353 $value = apply_filters( 'frm_display_' . $field->type . '_value_custom', $value, compact( 'field', 'atts' ) );
354
355 // phpcs:ignore Universal.Operators.StrictComparisons
356 if ( $value == $unfiltered_value ) {
357 $value = FrmFieldsHelper::get_unfiltered_display_value( compact( 'value', 'field', 'atts' ) );
358 }
359
360 if ( $atts['truncate'] && $atts['type'] !== 'url' ) {
361 $value = FrmAppHelper::truncate( $value, 50 );
362 }
363
364 if ( ! $atts['keepjs'] && ! is_array( $value ) ) {
365 $value = FrmAppHelper::kses( $value, 'all' );
366 }
367
368 return apply_filters( 'frm_display_value', $value, $field, $atts );
369 }
370
371 /**
372 * @param object $field
373 * @param mixed $value
374 * @param array $args
375 *
376 * @return void
377 */
378 public static function set_posted_value( $field, $value, $args ) {
379 // If validating a field with "other" opt, set back to prev value now.
380 if ( ! empty( $args['other'] ) ) {
381 $value = $args['temp_value'];
382 }
383
384 if ( empty( $args['parent_field_id'] ) ) {
385 $_POST['item_meta'][ $field->id ] = $value;
386 } else {
387 self::set_parent_field_posted_value( $field, $value, $args );
388 }
389 }
390
391 /**
392 * Init arrays if necessary, else we get fatal error.
393 *
394 * @since 4.01
395 *
396 * @param object $field Field object.
397 * @param mixed $value Value to set.
398 * @param array $args Additional arguments.
399 *
400 * @return void
401 */
402 private static function set_parent_field_posted_value( $field, $value, $args ) {
403 if ( isset( $_POST['item_meta'][ $args['parent_field_id'] ] ) && is_array( $_POST['item_meta'][ $args['parent_field_id'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
404 if ( ! isset( $_POST['item_meta'][ $args['parent_field_id'] ][ $args['key_pointer'] ] ) || ! is_array( $_POST['item_meta'][ $args['parent_field_id'] ][ $args['key_pointer'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
405 $_POST['item_meta'][ $args['parent_field_id'] ][ $args['key_pointer'] ] = array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
406 }
407 } else {
408 // All of the section was probably removed.
409 $_POST['item_meta'][ $args['parent_field_id'] ] = array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
410 $_POST['item_meta'][ $args['parent_field_id'] ][ $args['key_pointer'] ] = array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing
411 }
412
413 $_POST['item_meta'][ $args['parent_field_id'] ][ $args['key_pointer'] ][ $field->id ] = $value; // phpcs:ignore WordPress.Security.NonceVerification.Missing
414 }
415
416 /**
417 * @param array|int|object $field
418 * @param mixed $value
419 * @param array $args
420 *
421 * @return void
422 */
423 public static function get_posted_value( $field, &$value, $args ) {
424 if ( is_array( $field ) ) {
425 $field_id = $field['id'];
426 $field_obj = FrmFieldFactory::get_field_object( $field['id'] );
427 } elseif ( is_object( $field ) ) {
428 $field_id = $field->id;
429
430 if ( 'hidden' === $field->type && ! empty( $field->field_options['original_type'] ) ) {
431 $field_obj = FrmFieldFactory::get_field_type( $field->field_options['original_type'], $field );
432 } else {
433 $field_obj = FrmFieldFactory::get_field_object( $field );
434 }
435 } elseif ( is_numeric( $field ) ) {
436 $field_id = $field;
437 $field_obj = FrmFieldFactory::get_field_object( $field );
438 } else {
439 $value = self::get_posted_meta( $field, $args );
440 FrmAppHelper::sanitize_value( 'sanitize_text_field', $value );
441 return;
442 }
443
444 $value = self::get_posted_meta( $field_id, $args );
445
446 $field_obj->sanitize_value( $value );
447 }
448
449 /**
450 * @since 4.02.04
451 *
452 * @param int|string $field_id Field ID.
453 * @param array $args Additional arguments.
454 *
455 * @return mixed
456 */
457 private static function get_posted_meta( $field_id, $args ) {
458 if ( empty( $args['parent_field_id'] ) ) {
459 // Sanitizing is done next.
460 $value = isset( $_POST['item_meta'][ $field_id ] ) ? wp_unslash( $_POST['item_meta'][ $field_id ] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
461 } else {
462 $value = isset( $_POST['item_meta'][ $args['parent_field_id'] ][ $args['key_pointer'] ][ $field_id ] ) ? wp_unslash( $_POST['item_meta'][ $args['parent_field_id'] ][ $args['key_pointer'] ][ $field_id ] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
463 }
464 return $value;
465 }
466
467 /**
468 * Check if field has an "Other" option and if any other values are posted
469 *
470 * @since 2.0
471 *
472 * @param object $field Field object.
473 * @param array|string $value Field value, passed by reference.
474 * @param array $args Arguments array, passed by reference.
475 *
476 * @return void
477 */
478 public static function maybe_set_other_validation( $field, &$value, &$args ) {
479 $args['other'] = false;
480
481 if ( ! $value || ! FrmAppHelper::pro_is_installed() ) {
482 return;
483 }
484
485 // Trim excess values if selection limit is exceeded for checkbox. Necessary to do here
486 // as the value set here will be used later in this class's set_posted_value() method.
487 if ( FrmField::is_checkbox( $field ) ) {
488 $field_obj = FrmFieldFactory::get_field_object( $field );
489 $field_obj->maybe_trim_excess_values( $value );
490 }
491
492 // Get other value for fields in repeating section.
493 self::set_other_repeating_vals( $field, $value, $args );
494
495 // Check if there are any posted "Other" values.
496 if ( FrmField::is_option_true( $field, 'other' ) && isset( $_POST['item_meta']['other'][ $field->id ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
497
498 // Save original value.
499 $args['temp_value'] = $value;
500 $args['other'] = true;
501
502 // Sanitizing is done next.
503 $other_vals = wp_unslash( $_POST['item_meta']['other'][ $field->id ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
504 FrmAppHelper::sanitize_value( 'sanitize_text_field', $other_vals );
505
506 // Set the validation value now
507 self::set_other_validation_val( $value, $other_vals, $field, $args );
508 }
509 }
510
511 /**
512 * Sets radio or checkbox value equal to "other" value if it is set - FOR REPEATING SECTIONS
513 *
514 * @since 2.0
515 *
516 * @param object $field Field object.
517 * @param array|string $value Field value, passed by reference.
518 * @param array $args Arguments array, passed by reference.
519 *
520 * @return void
521 */
522 public static function set_other_repeating_vals( $field, &$value, &$args ) {
523 if ( ! $args['parent_field_id'] ) {
524 return;
525 }
526
527 // Check if there are any other posted "other" values for this field.
528 if ( FrmField::is_option_true( $field, 'other' ) && isset( $_POST['item_meta'][ $args['parent_field_id'] ][ $args['key_pointer'] ]['other'][ $field->id ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
529 // Save original value
530 $args['temp_value'] = $value;
531 $args['other'] = true;
532 $other_vals = wp_unslash( $_POST['item_meta'][ $args['parent_field_id'] ][ $args['key_pointer'] ]['other'][ $field->id ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
533 FrmAppHelper::sanitize_value( 'sanitize_text_field', $other_vals );
534
535 // Set the validation value now.
536 self::set_other_validation_val( $value, $other_vals, $field, $args );
537 }
538 }
539
540 /**
541 * Modify value used for validation
542 * This function essentially removes the "Other" radio or checkbox value from the $value being validated.
543 * It also adds any text from the free text fields to the value
544 *
545 * Needs to accommodate for times when other opt is selected, but no other free text is entered
546 *
547 * @since 2.0
548 *
549 * @param array|string $value
550 * @param array|string $other_vals (usually of posted values).
551 * @param object $field
552 * @param array $args
553 *
554 * @return void
555 */
556 public static function set_other_validation_val( &$value, $other_vals, $field, &$args ) {
557 // Checkboxes and multi-select dropdowns.
558 if ( is_array( $value ) && $field->type === 'checkbox' ) {
559 // Combine "Other" values with checked values. "Other" values will override checked box values.
560 foreach ( $other_vals as $k => $v ) {
561 if ( isset( $value[ $k ] ) && trim( $v ) === '' ) {
562 // If the other box is checked, but doesn't have a value.
563 $value = '';
564 break;
565 }
566 }
567
568 if ( is_array( $value ) && $value ) {
569 $value = array_merge( $value, $other_vals );
570 }
571 } else {
572 // Radio and dropdowns.
573 $other_key = array_filter( array_keys( $field->options ), 'is_string' );
574 $other_key = reset( $other_key );
575
576 // Multi-select dropdown.
577 if ( is_array( $value ) ) {
578 // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
579 $o_key = array_search( $field->options[ $other_key ], $value );
580
581 if ( $o_key !== false ) {
582 // Modify the original value so other key will be preserved.
583 $value[ $other_key ] = $value[ $o_key ];
584
585 // By default, the array keys will be numeric for multi-select dropdowns.
586 // If going backwards and forwards between pages, the array key will match the other key.
587 if ( $o_key !== $other_key ) {
588 unset( $value[ $o_key ] );
589 }
590
591 $args['temp_value'] = $value;
592 $value[ $other_key ] = reset( $other_vals );
593
594 if ( FrmAppHelper::is_empty_value( $value[ $other_key ] ) ) {
595 unset( $value[ $other_key ] );
596 }
597 }
598 } elseif ( $field->options[ $other_key ] == $value ) { // phpcs:ignore Universal.Operators.StrictComparisons
599 $value = $other_vals;
600 }//end if
601 }//end if
602 }
603
604 /**
605 * Add submitted values to a string for spam checking.
606 *
607 * @param array $values
608 *
609 * @return string
610 */
611 public static function entry_array_to_string( $values ) {
612 $content = '';
613
614 foreach ( $values['item_meta'] as $val ) {
615 if ( $content !== '' ) {
616 $content .= "\n\n";
617 }
618
619 if ( is_array( $val ) ) {
620 $val = FrmAppHelper::array_flatten( $val );
621 $val = implode( ', ', $val );
622 }
623
624 $content .= $val;
625 }
626
627 return $content;
628 }
629
630 /**
631 * Get the browser from the user agent
632 *
633 * @since 2.04
634 *
635 * @param string $u_agent
636 *
637 * @return string
638 */
639 public static function get_browser( $u_agent ) {
640 $bname = __( 'Unknown', 'formidable' );
641 $platform = $bname;
642 $ub = '';
643
644 // Get the operating system
645 if ( preg_match( '/windows|win32/i', $u_agent ) ) {
646 $platform = 'Windows';
647 } elseif ( preg_match( '/android/i', $u_agent ) ) {
648 $platform = 'Android';
649 } elseif ( preg_match( '/linux/i', $u_agent ) ) {
650 $platform = 'Linux';
651 } elseif ( preg_match( '/macintosh|mac os x/i', $u_agent ) ) {
652 $platform = 'OS X';
653 }
654
655 $agent_options = array(
656 'Chrome' => 'Google Chrome',
657 'Safari' => 'Apple Safari',
658 'Opera' => 'Opera',
659 'Netscape' => 'Netscape',
660 'Firefox' => 'Mozilla Firefox',
661 );
662
663 // Next get the name of the useragent yes separately and for good reason.
664 if ( str_contains( $u_agent, 'MSIE' ) && ! str_contains( $u_agent, 'Opera' ) ) {
665 $bname = 'Internet Explorer';
666 $ub = 'MSIE';
667 } else {
668 foreach ( $agent_options as $agent_key => $agent_name ) {
669 if ( str_contains( $u_agent, $agent_key ) ) {
670 $bname = $agent_name;
671 $ub = $agent_key;
672 break;
673 }
674 }
675 }
676
677 // finally get the correct version number
678 $known = array( 'Version', $ub, 'other' );
679 $pattern = '#(?<browser>' . implode( '|', $known ) . ')[/ ]+(?<version>[0-9.|a-zA-Z.]*)#';
680 // Get the matching numbers.
681 preg_match_all( $pattern, $u_agent, $matches );
682
683 // see how many we have
684 $i = count( $matches['browser'] );
685
686 if ( $i > 1 ) {
687 // We will have two since we are not using 'other' argument yet
688 // see if version is before or after the name.
689 $version = strripos( $u_agent, 'Version' ) < strripos( $u_agent, $ub ) ? $matches['version'][0] : $matches['version'][1];
690 } elseif ( $i === 1 ) {
691 $version = $matches['version'][0];
692 } else {
693 $version = '';
694 }
695
696 // check if we have a number
697 if ( $version === '' ) {
698 $version = '?';
699 }
700
701 return $bname . ' ' . $version . ' / ' . $platform;
702 }
703
704 /**
705 * @since 3.0
706 *
707 * @param array $atts Action dropdown attributes.
708 *
709 * @return void
710 */
711 public static function actions_dropdown( $atts ) {
712 $id = $atts['id'] ?? FrmAppHelper::get_param( 'id', 0, 'get', 'absint' );
713 $links = self::get_action_links( $id, $atts['entry'] );
714
715 foreach ( $links as $link ) {
716 ?>
717 <div class="misc-pub-section">
718 <a href="<?php echo esc_url( $link['url'] ); ?>"
719 <?php
720 if ( isset( $link['data'] ) ) {
721 foreach ( $link['data'] as $data => $value ) {
722 echo 'data-' . esc_attr( $data ) . '="' . esc_attr( $value ) . '" ';
723 }
724 }
725
726 if ( isset( $link['class'] ) ) {
727 echo 'class="' . esc_attr( $link['class'] ) . '" ';
728 }
729
730 if ( isset( $link['id'] ) ) {
731 echo 'id="' . esc_attr( $link['id'] ) . '" ';
732 }
733 ?>
734 >
735 <?php FrmAppHelper::icon_by_class( $link['icon'], array( 'aria-hidden' => 'true' ) ); ?>
736 <span class="frm_link_label"><?php echo esc_html( $link['label'] ); ?></span>
737 </a>
738 </div>
739 <?php
740 }//end foreach
741 }
742
743 /**
744 * @since 3.0
745 *
746 * @param int $id Entry ID.
747 * @param array|object $entry Entry object.
748 *
749 * @return array
750 */
751 private static function get_action_links( $id, $entry ) {
752 $page = FrmAppHelper::get_param( 'frm_action' );
753 $actions = array();
754
755 if ( $page !== 'show' ) {
756 $actions['frm_view'] = array(
757 'url' => admin_url( 'admin.php?page=formidable-entries&frm_action=show&id=' . $id . '&form=' . $entry->form_id ),
758 'label' => __( 'View Entry', 'formidable' ),
759 'icon' => 'frmfont frm_save_icon',
760 );
761 }
762
763 if ( current_user_can( 'frm_delete_entries' ) ) {
764 $actions['frm_delete'] = array(
765 'url' => wp_nonce_url( admin_url( 'admin.php?page=formidable-entries&frm_action=destroy&id=' . $id . '&form=' . $entry->form_id ) ),
766 'label' => __( 'Delete Entry', 'formidable' ),
767 'icon' => 'frmfont frm_delete_icon',
768 'data' => array(
769 'frmverify' => __( 'Permanently delete this entry?', 'formidable' ),
770 'frmverify-btn' => 'frm-button-red',
771 ),
772 );
773 }
774
775 if ( $page === 'show' ) {
776 $actions['frm_print'] = array(
777 'url' => '#',
778 'label' => __( 'Print Entry', 'formidable' ),
779 'data' => array(
780 'frmprint' => '1',
781 ),
782 'icon' => 'frmfont frm_printer_icon',
783 );
784 }
785
786 $actions['frm_resend'] = array(
787 'url' => '#',
788 'label' => __( 'Resend Emails', 'formidable' ),
789 'class' => 'frm_noallow',
790 'data' => array(
791 'upgrade' => __( 'Resend Emails', 'formidable' ),
792 'medium' => 'resend-email',
793 'content' => 'entry',
794 ),
795 'icon' => 'frmfont frm_email_icon',
796 );
797
798 if ( ! function_exists( 'frm_pdfs_autoloader' ) ) {
799 $actions['frm_download_pdf'] = array(
800 'url' => '#',
801 'label' => __( 'Download as PDF', 'formidable' ),
802 'class' => 'frm_noallow',
803 'data' => self::get_pdfs_upgrade_link_data( 'download-pdf-entry' ),
804 'icon' => 'frmfont frm_download_icon',
805 );
806 }
807
808 $actions['frm_edit'] = array(
809 'url' => '#',
810 'label' => __( 'Edit Entry', 'formidable' ),
811 'class' => 'frm_noallow',
812 'data' => array(
813 'upgrade' => __( 'Entry edits', 'formidable' ),
814 'medium' => 'edit-entries',
815 'content' => 'entry',
816 ),
817 'icon' => 'frmfont frm_pencil_icon',
818 );
819
820 return apply_filters( 'frm_entry_actions_dropdown', $actions, compact( 'id', 'entry' ) );
821 }
822
823 /**
824 * Gets data attributes for PDFs addon upgrade link.
825 *
826 * @param string $medium The source of the upgrade link used for analytics data.
827 *
828 * @return array
829 */
830 private static function get_pdfs_upgrade_link_data( $medium = 'pdfs' ) {
831 $data = array(
832 'oneclick' => '',
833 'requires' => '',
834 'upgrade' => __( 'Forms to PDF', 'formidable' ),
835 'medium' => $medium,
836 );
837
838 $upgrading = FrmAddonsController::install_link( 'pdfs' );
839
840 if ( isset( $upgrading['url'] ) ) {
841 $data['oneclick'] = json_encode( $upgrading );
842 } else {
843 $data['requires'] = FrmAddonsController::get_addon_required_plan( 28136428 );
844 }
845
846 return $data;
847 }
848
849 /**
850 * @since 5.0.15
851 *
852 * @param int|string $entry_id
853 *
854 * @return void
855 */
856 public static function maybe_render_captcha_score( $entry_id ) {
857 $query = array(
858 'item_id' => (int) $entry_id,
859 'field_id' => 0,
860 );
861 $metas_without_a_field = (array) FrmEntryMeta::getAll( $query, ' ORDER BY it.created_at DESC', '', true );
862
863 foreach ( $metas_without_a_field as $meta ) {
864 if ( ! empty( $meta->meta_value['captcha_score'] ) ) {
865 echo '<div class="misc-pub-section">';
866 FrmAppHelper::icon_by_class( 'frmfont frm_shield_check_icon', array( 'aria-hidden' => 'true' ) );
867 echo ' ' . esc_html__( 'reCAPTCHA Score', 'formidable' ) . ': ';
868 echo '<b>' . esc_html( $meta->meta_value['captcha_score'] ) . '</b>';
869 echo '</div>';
870 return;
871 }
872 }
873 }
874
875 /**
876 * Return entry status based on is_draft column value.
877 *
878 * @since 6.5
879 *
880 * @param int $status is_draft column.
881 *
882 * @return int
883 */
884 public static function get_entry_status( $status ) {
885 $statuses = self::get_entry_statuses();
886
887 if ( array_key_exists( $status, $statuses ) ) {
888 return $status;
889 }
890
891 if ( ! $status ) {
892 // If the status is empty, let's default to 0.
893 return self::SUBMITTED_ENTRY_STATUS;
894 }
895
896 // If it has a value that isn't in the array, let's default to 1. There may be old entries that don't have a value for is_draft.
897 return self::DRAFT_ENTRY_STATUS;
898 }
899
900 /**
901 * Return entry status label based on passed value.
902 *
903 * @since 6.5
904 *
905 * @param int $status is_draft column.
906 *
907 * @return string
908 */
909 public static function get_entry_status_label( $status ) {
910 $statuses = self::get_entry_statuses();
911 return $statuses[ self::get_entry_status( $status ) ];
912 }
913
914 /**
915 * Get all entry statuses.
916 *
917 * @since 6.5
918 * @since 6.6 function went from private to public.
919 *
920 * @return array<string>
921 */
922 public static function get_entry_statuses() {
923 $default_entry_statuses = array(
924 self::SUBMITTED_ENTRY_STATUS => __( 'Submitted', 'formidable' ),
925 self::DRAFT_ENTRY_STATUS => __( 'Draft', 'formidable' ),
926 );
927
928 /**
929 * Register entry status.
930 *
931 * "2" is used in abandonment-addon and reserved for "In progress".
932 * "3" is used in abandonment-addon and reserved for "Abandoned".
933 *
934 * @since 6.5
935 *
936 * @param array<string> $extended_entry_status Entry statuses.
937 */
938 $extended_entry_status = apply_filters( 'frm_entry_statuses', array() );
939
940 if ( ! is_array( $extended_entry_status ) ) {
941 _doing_it_wrong( __METHOD__, esc_html__( 'Entry status must be return in array format.', 'formidable' ), '6.5' );
942 $extended_entry_status = array();
943 }
944
945 return array_replace( $default_entry_statuses, $extended_entry_status );
946 }
947
948 /**
949 * @since 6.17
950 *
951 * @return int
952 */
953 public static function get_visible_unread_inbox_count() {
954 $menu_name = FrmAppHelper::get_menu_name();
955
956 if ( ! in_array( $menu_name, array( 'Formidable', 'Forms' ), true ) ) {
957 return 0;
958 }
959
960 $inbox = new FrmInbox();
961 $inbox_count = count( $inbox->unread() );
962
963 if ( ! $inbox_count ) {
964 return 0;
965 }
966
967 if ( is_callable( 'FrmProSettingsController::inbox_badge' ) ) {
968 $inbox_count = FrmProSettingsController::inbox_badge( $inbox_count );
969
970 if ( ! $inbox_count ) {
971 return 0;
972 }
973 }
974
975 return $inbox_count;
976 }
977 }
978