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

989 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 = 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 *
454 * @param int|string $field_id Field ID.
455 * @param array $args Additional arguments.
456 *
457 * @return mixed
458 */
459 private static function get_posted_meta( $field_id, $args ) {
460 if ( empty( $args['parent_field_id'] ) ) {
461 // Sanitizing is done next.
462 $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
463 } else {
464 $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
465 }
466 return $value;
467 }
468
469 /**
470 * Check if field has an "Other" option and if any other values are posted
471 *
472 * @since 2.0
473 *
474 * @param object $field Field object.
475 * @param array|string $value Field value, passed by reference.
476 * @param array $args Arguments array, passed by reference.
477 *
478 * @return void
479 */
480 public static function maybe_set_other_validation( $field, &$value, &$args ) {
481 $args['other'] = false;
482
483 if ( ! $value || ! FrmAppHelper::pro_is_installed() ) {
484 return;
485 }
486
487 // Trim excess values if selection limit is exceeded for checkbox. Necessary to do here
488 // as the value set here will be used later in this class's set_posted_value() method.
489 if ( FrmField::is_checkbox( $field ) ) {
490 $field_obj = FrmFieldFactory::get_field_object( $field );
491 $field_obj->maybe_trim_excess_values( $value );
492 }
493
494 // Get other value for fields in repeating section.
495 self::set_other_repeating_vals( $field, $value, $args );
496
497 // Check if there are any posted "Other" values.
498 if ( ! FrmField::is_option_true( $field, 'other' ) || ! isset( $_POST['item_meta']['other'][ $field->id ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
499 return;
500 }
501
502 // Save original value.
503 $args['temp_value'] = $value;
504 $args['other'] = true;
505
506 // Sanitizing is done next.
507 $other_vals = wp_unslash( $_POST['item_meta']['other'][ $field->id ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing, SlevomatCodingStandard.Files.LineLength.LineTooLong
508 FrmAppHelper::sanitize_value( 'sanitize_text_field', $other_vals );
509
510 // Set the validation value now
511 self::set_other_validation_val( $value, $other_vals, $field, $args );
512 }
513
514 /**
515 * Sets radio or checkbox value equal to "other" value if it is set - FOR REPEATING SECTIONS
516 *
517 * @since 2.0
518 *
519 * @param object $field Field object.
520 * @param array|string $value Field value, passed by reference.
521 * @param array $args Arguments array, passed by reference.
522 *
523 * @return void
524 */
525 public static function set_other_repeating_vals( $field, &$value, &$args ) {
526 if ( ! $args['parent_field_id'] ) {
527 return;
528 }
529
530 // Check if there are any other posted "other" values for this field.
531 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
532 return;
533 }
534
535 // Save original value
536 $args['temp_value'] = $value;
537 $args['other'] = true;
538 $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
539 FrmAppHelper::sanitize_value( 'sanitize_text_field', $other_vals );
540
541 // Set the validation value now.
542 self::set_other_validation_val( $value, $other_vals, $field, $args );
543 }
544
545 /**
546 * Modify value used for validation
547 * This function essentially removes the "Other" radio or checkbox value from the $value being validated.
548 * It also adds any text from the free text fields to the value
549 *
550 * Needs to accommodate for times when other opt is selected, but no other free text is entered
551 *
552 * @since 2.0
553 *
554 * @param array|string $value
555 * @param array|string $other_vals (usually of posted values).
556 * @param object $field
557 * @param array $args
558 *
559 * @return void
560 */
561 public static function set_other_validation_val( &$value, $other_vals, $field, &$args ) {
562 // Checkboxes.
563 if ( is_array( $value ) && $field->type === 'checkbox' ) {
564 // Combine "Other" values with checked values. "Other" values will override checked box values.
565 foreach ( $other_vals as $k => $v ) {
566 if ( isset( $value[ $k ] ) && trim( $v ) === '' ) {
567 // If the other box is checked, but doesn't have a value.
568 $value = '';
569 break;
570 }
571 }
572
573 if ( is_array( $value ) && $value ) {
574 $value = array_merge( $value, $other_vals );
575 }
576
577 return;
578 }
579
580 // Radio and dropdowns.
581 $other_key = array_filter( array_keys( $field->options ), 'is_string' );
582 $other_key = reset( $other_key );
583
584 // Multi-select dropdown.
585 if ( is_array( $value ) ) {
586 // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
587 $o_key = array_search( $field->options[ $other_key ], $value );
588
589 if ( $o_key !== false ) {
590 // Modify the original value so other key will be preserved.
591 $value[ $other_key ] = $value[ $o_key ];
592
593 // By default, the array keys will be numeric for multi-select dropdowns.
594 // If going backwards and forwards between pages, the array key will match the other key.
595 if ( $o_key !== $other_key ) {
596 unset( $value[ $o_key ] );
597 }
598
599 $args['temp_value'] = $value;
600 $value[ $other_key ] = reset( $other_vals );
601
602 if ( FrmAppHelper::is_empty_value( $value[ $other_key ] ) ) {
603 unset( $value[ $other_key ] );
604 }
605 }
606 } elseif ( $field->options[ $other_key ] == $value ) { // phpcs:ignore Universal.Operators.StrictComparisons
607 $value = $other_vals;
608 }//end if
609 }
610
611 /**
612 * Add submitted values to a string for spam checking.
613 *
614 * @param array $values
615 *
616 * @return string
617 */
618 public static function entry_array_to_string( $values ) {
619 $content = '';
620
621 foreach ( $values['item_meta'] as $val ) {
622 if ( $content !== '' ) {
623 $content .= "\n\n";
624 }
625
626 if ( is_array( $val ) ) {
627 $val = FrmAppHelper::array_flatten( $val );
628 $val = implode( ', ', $val );
629 }
630
631 $content .= $val;
632 }
633
634 return $content;
635 }
636
637 /**
638 * Get the browser from the user agent
639 *
640 * @since 2.04
641 *
642 * @param string $u_agent
643 *
644 * @return string
645 */
646 public static function get_browser( $u_agent ) {
647 $bname = __( 'Unknown', 'formidable' );
648 $platform = $bname;
649 $ub = '';
650
651 // Get the operating system
652 if ( preg_match( '/windows|win32/i', $u_agent ) ) {
653 $platform = 'Windows';
654 } elseif ( preg_match( '/android/i', $u_agent ) ) {
655 $platform = 'Android';
656 } elseif ( preg_match( '/linux/i', $u_agent ) ) {
657 $platform = 'Linux';
658 } elseif ( preg_match( '/macintosh|mac os x/i', $u_agent ) ) {
659 $platform = 'OS X';
660 }
661
662 $agent_options = array(
663 'Chrome' => 'Google Chrome',
664 'Safari' => 'Apple Safari',
665 'Opera' => 'Opera',
666 'Netscape' => 'Netscape',
667 'Firefox' => 'Mozilla Firefox',
668 );
669
670 // Next get the name of the useragent yes separately and for good reason.
671 if ( str_contains( $u_agent, 'MSIE' ) && ! str_contains( $u_agent, 'Opera' ) ) {
672 $bname = 'Internet Explorer';
673 $ub = 'MSIE';
674 } else {
675 foreach ( $agent_options as $agent_key => $agent_name ) {
676 if ( str_contains( $u_agent, $agent_key ) ) {
677 $bname = $agent_name;
678 $ub = $agent_key;
679 break;
680 }
681 }
682 }
683
684 // Finally get the correct version number
685 $known = array( 'Version', $ub, 'other' );
686 $pattern = '#(?<browser>' . implode( '|', $known ) . ')[/ ]+(?<version>[0-9.|a-zA-Z.]*)#';
687 // Get the matching numbers.
688 preg_match_all( $pattern, $u_agent, $matches );
689
690 // See how many we have
691 $i = count( $matches['browser'] );
692
693 if ( $i > 1 ) {
694 // We will have two since we are not using 'other' argument yet
695 // See if version is before or after the name.
696 $version = strripos( $u_agent, 'Version' ) < strripos( $u_agent, $ub ) ? $matches['version'][0] : $matches['version'][1];
697 } elseif ( $i === 1 ) {
698 $version = $matches['version'][0];
699 } else {
700 $version = '';
701 }
702
703 // Check if we have a number
704 if ( $version === '' ) {
705 $version = '?';
706 }
707
708 return $bname . ' ' . $version . ' / ' . $platform;
709 }
710
711 /**
712 * @since 3.0
713 *
714 * @param array $atts Action dropdown attributes.
715 *
716 * @return void
717 */
718 public static function actions_dropdown( $atts ) {
719 $id = $atts['id'] ?? FrmAppHelper::get_param( 'id', 0, 'get', 'absint' );
720 $links = self::get_action_links( $id, $atts['entry'] );
721
722 foreach ( $links as $link ) {
723 // phpcs:disable Generic.WhiteSpace.ScopeIndent
724 ?>
725 <div class="misc-pub-section">
726 <a href="<?php echo esc_url( $link['url'] ); ?>"
727 <?php
728 if ( isset( $link['data'] ) ) {
729 foreach ( $link['data'] as $data => $value ) {
730 echo 'data-' . esc_attr( $data ) . '="' . esc_attr( $value ) . '" ';
731 }
732 }
733
734 if ( isset( $link['class'] ) ) {
735 echo 'class="' . esc_attr( $link['class'] ) . '" ';
736 }
737
738 if ( isset( $link['id'] ) ) {
739 echo 'id="' . esc_attr( $link['id'] ) . '" ';
740 }
741 ?>
742 >
743 <?php FrmAppHelper::icon_by_class( $link['icon'], array( 'aria-hidden' => 'true' ) ); ?>
744 <span class="frm_link_label"><?php echo esc_html( $link['label'] ); ?></span>
745 </a>
746 </div>
747 <?php
748 // phpcs:enable Generic.WhiteSpace.ScopeIndent
749 }//end foreach
750 }
751
752 /**
753 * @since 3.0
754 *
755 * @param int $id Entry ID.
756 * @param array|object $entry Entry object.
757 *
758 * @return array
759 */
760 private static function get_action_links( $id, $entry ) {
761 $page = FrmAppHelper::get_param( 'frm_action' );
762 $actions = array();
763
764 if ( $page !== 'show' ) {
765 $actions['frm_view'] = array(
766 'url' => admin_url( 'admin.php?page=formidable-entries&frm_action=show&id=' . $id . '&form=' . $entry->form_id ),
767 'label' => __( 'View Entry', 'formidable' ),
768 'icon' => 'frmfont frm_save_icon',
769 );
770 }
771
772 if ( current_user_can( 'frm_delete_entries' ) ) {
773 $actions['frm_delete'] = array(
774 'url' => wp_nonce_url( admin_url( 'admin.php?page=formidable-entries&frm_action=destroy&id=' . $id . '&form=' . $entry->form_id ) ),
775 'label' => __( 'Delete Entry', 'formidable' ),
776 'icon' => 'frmfont frm_delete_icon',
777 'data' => array(
778 'frmverify' => __( 'Permanently delete this entry?', 'formidable' ),
779 'frmverify-btn' => 'frm-button-red',
780 ),
781 );
782 }
783
784 if ( $page === 'show' ) {
785 $actions['frm_print'] = array(
786 'url' => '#',
787 'label' => __( 'Print Entry', 'formidable' ),
788 'data' => array(
789 'frmprint' => '1',
790 ),
791 'icon' => 'frmfont frm_printer_icon',
792 );
793 }
794
795 $actions['frm_resend'] = array(
796 'url' => '#',
797 'label' => __( 'Resend Emails', 'formidable' ),
798 'class' => 'frm_noallow',
799 'data' => array(
800 'upgrade' => __( 'Resend Emails', 'formidable' ),
801 'medium' => 'resend-email',
802 'content' => 'entry',
803 ),
804 'icon' => 'frmfont frm_email_icon',
805 );
806
807 if ( ! function_exists( 'frm_pdfs_autoloader' ) ) {
808 $actions['frm_download_pdf'] = array(
809 'url' => '#',
810 'label' => __( 'Download as PDF', 'formidable' ),
811 'class' => 'frm_noallow',
812 'data' => self::get_pdfs_upgrade_link_data( 'download-pdf-entry' ),
813 'icon' => 'frmfont frm_download_icon',
814 );
815 }
816
817 $actions['frm_edit'] = array(
818 'url' => '#',
819 'label' => __( 'Edit Entry', 'formidable' ),
820 'class' => 'frm_noallow',
821 'data' => array(
822 'upgrade' => __( 'Entry edits', 'formidable' ),
823 'medium' => 'edit-entries',
824 'content' => 'entry',
825 ),
826 'icon' => 'frmfont frm_pencil_icon',
827 );
828
829 return apply_filters( 'frm_entry_actions_dropdown', $actions, compact( 'id', 'entry' ) );
830 }
831
832 /**
833 * Gets data attributes for PDFs addon upgrade link.
834 *
835 * @param string $medium The source of the upgrade link used for analytics data.
836 *
837 * @return array
838 */
839 private static function get_pdfs_upgrade_link_data( $medium = 'pdfs' ) {
840 $data = array(
841 'oneclick' => '',
842 'requires' => '',
843 'upgrade' => __( 'Forms to PDF', 'formidable' ),
844 'medium' => $medium,
845 );
846
847 $upgrading = FrmAddonsController::install_link( 'pdfs' );
848
849 if ( isset( $upgrading['url'] ) ) {
850 $data['oneclick'] = json_encode( $upgrading );
851 } else {
852 $data['requires'] = FrmAddonsController::get_addon_required_plan( 28136428 );
853 }
854
855 return $data;
856 }
857
858 /**
859 * @since 5.0.15
860 *
861 * @param int|string $entry_id
862 *
863 * @return void
864 */
865 public static function maybe_render_captcha_score( $entry_id ) {
866 $query = array(
867 'item_id' => (int) $entry_id,
868 'field_id' => 0,
869 );
870 $metas_without_a_field = (array) FrmEntryMeta::getAll( $query, ' ORDER BY it.created_at DESC', '', true );
871
872 foreach ( $metas_without_a_field as $meta ) {
873 if ( empty( $meta->meta_value['captcha_score'] ) ) {
874 continue;
875 }
876
877 echo '<div class="misc-pub-section">';
878 FrmAppHelper::icon_by_class( 'frmfont frm_shield_check_icon', array( 'aria-hidden' => 'true' ) );
879 echo ' ' . esc_html__( 'reCAPTCHA Score', 'formidable' ) . ': ';
880 echo '<b>' . esc_html( $meta->meta_value['captcha_score'] ) . '</b>';
881 echo '</div>';
882 return;
883 }
884 }
885
886 /**
887 * Return entry status based on is_draft column value.
888 *
889 * @since 6.5
890 *
891 * @param int $status is_draft column.
892 *
893 * @return int
894 */
895 public static function get_entry_status( $status ) {
896 $statuses = self::get_entry_statuses();
897
898 if ( array_key_exists( $status, $statuses ) ) {
899 return $status;
900 }
901
902 if ( ! $status ) {
903 // If the status is empty, let's default to 0.
904 return self::SUBMITTED_ENTRY_STATUS;
905 }
906
907 // 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.
908 return self::DRAFT_ENTRY_STATUS;
909 }
910
911 /**
912 * Return entry status label based on passed value.
913 *
914 * @since 6.5
915 *
916 * @param int $status is_draft column.
917 *
918 * @return string
919 */
920 public static function get_entry_status_label( $status ) {
921 $statuses = self::get_entry_statuses();
922 return $statuses[ self::get_entry_status( $status ) ];
923 }
924
925 /**
926 * Get all entry statuses.
927 *
928 * @since 6.5
929 * @since 6.6 function went from private to public.
930 *
931 * @return array<string>
932 */
933 public static function get_entry_statuses() {
934 $default_entry_statuses = array(
935 self::SUBMITTED_ENTRY_STATUS => __( 'Submitted', 'formidable' ),
936 self::DRAFT_ENTRY_STATUS => __( 'Draft', 'formidable' ),
937 );
938
939 /**
940 * Register entry status.
941 *
942 * "2" is used in abandonment-addon and reserved for "In progress".
943 * "3" is used in abandonment-addon and reserved for "Abandoned".
944 *
945 * @since 6.5
946 *
947 * @param array<string> $extended_entry_status Entry statuses.
948 */
949 $extended_entry_status = apply_filters( 'frm_entry_statuses', array() );
950
951 if ( ! is_array( $extended_entry_status ) ) {
952 _doing_it_wrong( __METHOD__, esc_html__( 'Entry status must be return in array format.', 'formidable' ), '6.5' );
953 $extended_entry_status = array();
954 }
955
956 return array_replace( $default_entry_statuses, $extended_entry_status );
957 }
958
959 /**
960 * @since 6.17
961 *
962 * @return int
963 */
964 public static function get_visible_unread_inbox_count() {
965 $menu_name = FrmAppHelper::get_menu_name();
966
967 if ( ! in_array( $menu_name, array( 'Formidable', 'Forms' ), true ) ) {
968 return 0;
969 }
970
971 $inbox = new FrmInbox();
972 $inbox_count = count( $inbox->unread() );
973
974 if ( ! $inbox_count ) {
975 return 0;
976 }
977
978 if ( is_callable( 'FrmProSettingsController::inbox_badge' ) ) {
979 $inbox_count = FrmProSettingsController::inbox_badge( $inbox_count );
980
981 if ( ! $inbox_count ) {
982 return 0;
983 }
984 }
985
986 return $inbox_count;
987 }
988 }
989