PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.31
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.31
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.31, at classes/helpers/FrmEntriesHelper.php

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