PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.9.1
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.9.1
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 / FrmXMLHelper.php

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

2,126 lines 63.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 FrmXMLHelper {
7
8 /**
9 * @var bool true if importing an XML from API, false if importing an XML file manually.
10 */
11 private static $installing_template = false;
12
13 public static function get_xml_values( $opt, $padding ) {
14 if ( is_array( $opt ) ) {
15 foreach ( $opt as $ok => $ov ) {
16 echo "\n" . esc_html( $padding );
17 $tag = ( is_numeric( $ok ) ? 'key:' : '' ) . $ok;
18 echo '<' . esc_html( $tag ) . '>';
19 self::get_xml_values( $ov, $padding . ' ' );
20 if ( is_array( $ov ) ) {
21 echo "\n" . esc_html( $padding );
22 }
23 echo '</' . esc_html( $tag ) . '>';
24 }
25 } else {
26 echo self::cdata( $opt ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
27 }
28 }
29
30 public static function import_xml( $file ) {
31 if ( ! defined( 'WP_IMPORTING' ) ) {
32 define( 'WP_IMPORTING', true );
33 }
34
35 if ( ! class_exists( 'DOMDocument' ) ) {
36 $error_message = sprintf(
37 /* translators: 1: Documentation link */
38 __( 'In order to install XML, your server must have DOMDocument installed. Follow our documentation on %1$s to ensure DOMDocument is properly set up and XML support is enabled.', 'formidable' ),
39 '<a href="https://formidableforms.com/knowledgebase/import-forms-entries-and-views/#kb-your-server-does-not-have-xml-enabled" target="_blank">Importing Forms, Entries, and Views</a>'
40 );
41
42 return new WP_Error( 'SimpleXML_parse_error', $error_message, libxml_get_errors() );
43 }
44
45 $xml_string = file_get_contents( $file );
46 self::maybe_fix_xml( $xml_string );
47
48 $dom = new DOMDocument();
49
50 // LIBXML_COMPACT activates small nodes allocation optimization.
51 // Use LIBXML_PARSEHUGE to avoid "parser error : internal error: Huge input lookup" for large (300MB) files.
52 $success = $dom->loadXML( $xml_string, LIBXML_COMPACT | LIBXML_PARSEHUGE );
53 if ( ! $success ) {
54 return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this XML file', 'formidable' ), libxml_get_errors() );
55 }
56
57 if ( ! function_exists( 'simplexml_import_dom' ) ) {
58 return new WP_Error( 'SimpleXML_parse_error', __( 'Your server is missing the simplexml_import_dom function', 'formidable' ), libxml_get_errors() );
59 }
60
61 $xml = simplexml_import_dom( $dom );
62 unset( $dom );
63
64 // halt if loading produces an error
65 if ( ! $xml ) {
66 return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this XML file', 'formidable' ), libxml_get_errors() );
67 }
68
69 return self::import_xml_now( $xml );
70 }
71
72 /**
73 * @since 6.2.3
74 *
75 * @param string $xml_string
76 * @return void
77 */
78 private static function maybe_fix_xml( &$xml_string ) {
79 if ( '<?xml' !== substr( $xml_string, 0, 5 ) ) {
80 // Some XML files have may have unexpected characters at the start.
81 $xml_string = substr( $xml_string, strpos( $xml_string, '<?xml' ) );
82 }
83
84 // The Equity theme adds a <meta name="generator" content="Equity 1.7.13" /> tag using the "the_generator" filter.
85 // Strip that out as it breaks the XML import.
86 $channel_start_position = strpos( $xml_string, '<channel>' );
87 $content_before_channel_tag = substr( $xml_string, 0, $channel_start_position );
88 if ( 0 !== strpos( $content_before_channel_tag, '<meta name="generator" ' ) ) {
89 $content_before_channel_tag = preg_replace(
90 '/<meta\s+[^>]*name="generator"[^>]*\/>/i',
91 '',
92 $content_before_channel_tag,
93 1
94 );
95 $xml_string = $content_before_channel_tag . substr( $xml_string, $channel_start_position );
96 }
97 }
98
99 /**
100 * Add terms, forms (form and field ids), posts (post ids), and entries to db, in that order
101 *
102 * @since 3.06
103 *
104 * @param object $xml
105 * @param bool $installing_template
106 * @return array The number of items imported
107 */
108 public static function import_xml_now( $xml, $installing_template = false ) {
109 if ( ! defined( 'WP_IMPORTING' ) ) {
110 define( 'WP_IMPORTING', true );
111 }
112
113 self::$installing_template = $installing_template;
114 $imported = self::pre_import_data();
115
116 foreach ( array( 'term', 'form', 'view' ) as $item_type ) {
117 // Grab cats, tags, and terms, or forms or posts.
118 if ( isset( $xml->{$item_type} ) ) {
119 $function_name = 'import_xml_' . $item_type . 's';
120 $imported = self::$function_name( $xml->{$item_type}, $imported );
121 unset( $function_name, $xml->{$item_type} );
122 }
123 }
124
125 $imported = apply_filters( 'frm_importing_xml', $imported, $xml );
126
127 if ( empty( $imported['form_status'] ) ) {
128 // Check for an error message in the XML.
129 if ( isset( $xml->Code ) && isset( $xml->Message ) ) { // phpcs:ignore WordPress.NamingConventions
130 $imported['error'] = (string) $xml->Message; // phpcs:ignore WordPress.NamingConventions
131 }
132 }
133
134 return $imported;
135 }
136
137 /**
138 * @since 3.06
139 * @return array
140 */
141 private static function pre_import_data() {
142 $defaults = array(
143 'forms' => 0,
144 'fields' => 0,
145 'terms' => 0,
146 'posts' => 0,
147 'views' => 0,
148 'actions' => 0,
149 'styles' => 0,
150 );
151
152 return array(
153 'imported' => $defaults,
154 'updated' => $defaults,
155 'forms' => array(),
156 'terms' => array(),
157 );
158 }
159
160 public static function import_xml_terms( $terms, $imported ) {
161 foreach ( $terms as $t ) {
162 if ( term_exists( (string) $t->term_slug, (string) $t->term_taxonomy ) ) {
163 continue;
164 }
165
166 $parent = self::get_term_parent_id( $t );
167
168 $term = wp_insert_term(
169 (string) $t->term_name,
170 (string) $t->term_taxonomy,
171 array(
172 'slug' => (string) $t->term_slug,
173 'description' => (string) $t->term_description,
174 'parent' => empty( $parent ) ? 0 : $parent,
175 )
176 );
177
178 if ( $term && is_array( $term ) ) {
179 ++$imported['imported']['terms'];
180 $imported['terms'][ (int) $t->term_id ] = $term['term_id'];
181 }
182
183 unset( $term, $t );
184 }//end foreach
185
186 return $imported;
187 }
188
189 /**
190 * @since 2.0.8
191 */
192 private static function get_term_parent_id( $t ) {
193 $parent = (string) $t->term_parent;
194 if ( ! empty( $parent ) ) {
195 $parent = term_exists( (string) $t->term_parent, (string) $t->term_taxonomy );
196 if ( $parent ) {
197 $parent = $parent['term_id'];
198 } else {
199 $parent = 0;
200 }
201 }
202
203 return $parent;
204 }
205
206 public static function import_xml_forms( $forms, $imported ) {
207 $child_forms = array();
208
209 // Import child forms first
210 self::put_child_forms_first( $forms );
211
212 foreach ( $forms as $item ) {
213 $form = self::fill_form( $item );
214
215 self::update_custom_style_setting_on_import( $form );
216
217 $this_form = self::maybe_get_form( $form );
218
219 $old_id = false;
220 $form_fields = false;
221 if ( ! empty( $this_form ) ) {
222 $form_id = $this_form->id;
223 $old_id = $this_form->id;
224 self::update_form( $this_form, $form, $imported );
225
226 $form_fields = self::get_form_fields( $form_id );
227 } else {
228 $form_id = FrmForm::create( $form );
229 if ( $form_id ) {
230 if ( empty( $form['parent_form_id'] ) ) {
231 // Don't include the repeater form in the imported count.
232 ++$imported['imported']['forms'];
233 }
234
235 // Keep track of whether this specific form was updated or not.
236 $imported['form_status'][ $form_id ] = 'imported';
237 }
238 }
239
240 if ( $form_id ) {
241 self::track_imported_child_forms( (int) $form_id, $form['parent_form_id'], $child_forms );
242 }
243
244 self::import_xml_fields( $item->field, $form_id, $this_form, $form_fields, $imported );
245
246 self::delete_removed_fields( $form_fields );
247
248 // Update field ids/keys to new ones.
249 do_action( 'frm_after_duplicate_form', $form_id, $form, array( 'old_id' => $old_id ) );
250
251 $imported['forms'][ (int) $item->id ] = $form_id;
252
253 // Send pre 2.0 form options through function that creates actions.
254 self::migrate_form_settings_to_actions( $form['options'], $form_id, $imported, true );
255
256 do_action( 'frm_after_import_form', $form_id, $form );
257
258 unset( $form, $item );
259 }//end foreach
260
261 self::maybe_update_child_form_parent_id( $imported['forms'], $child_forms );
262
263 return $imported;
264 }
265
266 private static function fill_form( $item ) {
267 $form = array(
268 'id' => (int) $item->id,
269 'form_key' => (string) $item->form_key,
270 'name' => (string) $item->name,
271 'description' => (string) $item->description,
272 'options' => (string) $item->options,
273 'logged_in' => (int) $item->logged_in,
274 'is_template' => (int) $item->is_template,
275 'editable' => (int) $item->editable,
276 'status' => (string) $item->status,
277 'parent_form_id' => isset( $item->parent_form_id ) ? (int) $item->parent_form_id : 0,
278 'created_at' => gmdate( 'Y-m-d H:i:s', strtotime( (string) $item->created_at ) ),
279 );
280
281 if ( empty( $item->created_at ) ) {
282 $form['created_at'] = current_time( 'mysql', 1 );
283 }
284
285 $form['options'] = FrmAppHelper::maybe_json_decode( $form['options'] );
286
287 if ( self::$installing_template ) {
288 // Templates don't necessarily have antispam on, but we want our templates to all have antispam on by default.
289 $form['options']['antispam'] = 1;
290 }
291
292 return $form;
293 }
294
295 private static function maybe_get_form( $form ) {
296 // if template, allow to edit if form keys match, otherwise, creation date must also match
297 $edit_query = array(
298 'form_key' => $form['form_key'],
299 'is_template' => $form['is_template'],
300 );
301 if ( ! $form['is_template'] ) {
302 $edit_query['created_at'] = $form['created_at'];
303 }
304
305 $edit_query = apply_filters( 'frm_match_xml_form', $edit_query, $form );
306
307 $form = FrmForm::getAll( $edit_query, '', 1 );
308 if ( is_object( $form ) && $form->status === 'trash' ) {
309 FrmForm::destroy( $form->id );
310 return false;
311 }
312
313 return $form;
314 }
315
316 private static function update_form( $this_form, $form, &$imported ) {
317 $form_id = $this_form->id;
318 FrmForm::update( $form_id, $form );
319 if ( empty( $form['parent_form_id'] ) ) {
320 // Don't include the repeater form in the updated count.
321 ++$imported['updated']['forms'];
322 }
323
324 // Keep track of whether this specific form was updated or not
325 $imported['form_status'][ $form_id ] = 'updated';
326 }
327
328 private static function get_form_fields( $form_id ) {
329 $form_fields = FrmField::get_all_for_form( $form_id, '', 'exclude', 'exclude' );
330 $old_fields = array();
331 foreach ( $form_fields as $f ) {
332 $old_fields[ $f->id ] = $f;
333 $old_fields[ $f->field_key ] = $f->id;
334 unset( $f );
335 }
336 $form_fields = $old_fields;
337
338 return $form_fields;
339 }
340
341 /**
342 * Delete any fields attached to this form that were not included in the template
343 */
344 private static function delete_removed_fields( $form_fields ) {
345 if ( ! empty( $form_fields ) ) {
346 foreach ( $form_fields as $field ) {
347 if ( is_object( $field ) ) {
348 FrmField::destroy( $field->id );
349 }
350 unset( $field );
351 }
352 }
353 }
354
355 /**
356 * Put child forms first so they will be imported before parents
357 *
358 * @since 2.0.16
359 *
360 * @param array $forms
361 */
362 private static function put_child_forms_first( &$forms ) {
363 $child_forms = array();
364 $regular_forms = array();
365
366 foreach ( $forms as $form ) {
367 $parent_form_id = isset( $form->parent_form_id ) ? (int) $form->parent_form_id : 0;
368
369 if ( $parent_form_id ) {
370 $child_forms[] = $form;
371 } else {
372 $regular_forms[] = $form;
373 }
374 }
375
376 $forms = array_merge( $child_forms, $regular_forms );
377 }
378
379 /**
380 * Keep track of all imported child forms
381 *
382 * @since 2.0.16
383 *
384 * @param int $form_id
385 * @param int $parent_form_id
386 * @param array $child_forms
387 */
388 private static function track_imported_child_forms( $form_id, $parent_form_id, &$child_forms ) {
389 if ( $parent_form_id ) {
390 $child_forms[ $form_id ] = $parent_form_id;
391 }
392 }
393
394 /**
395 * Update the parent_form_id on imported child forms
396 * Child forms are imported first so their parent_form_id will need to be updated after the parent is imported
397 *
398 * @since 2.0.6
399 *
400 * @param array $imported_forms
401 * @param array $child_forms
402 */
403 private static function maybe_update_child_form_parent_id( $imported_forms, $child_forms ) {
404 foreach ( $child_forms as $child_form_id => $old_parent_form_id ) {
405 if ( isset( $imported_forms[ $old_parent_form_id ] ) && (int) $imported_forms[ $old_parent_form_id ] !== (int) $old_parent_form_id ) {
406 // Update all children with this old parent_form_id
407 $new_parent_form_id = (int) $imported_forms[ $old_parent_form_id ];
408 FrmForm::update( $child_form_id, array( 'parent_form_id' => $new_parent_form_id ) );
409 do_action( 'frm_update_child_form_parent_id', $child_form_id, $new_parent_form_id );
410 }
411 }
412 }
413
414 /**
415 * Import all fields for a form
416 *
417 * @since 2.0.13
418 *
419 * TODO: Cut down on params
420 */
421 private static function import_xml_fields( $xml_fields, $form_id, $this_form, &$form_fields, &$imported ) {
422 $in_section = 0;
423 $keys_by_original_field_id = array();
424
425 foreach ( $xml_fields as $field ) {
426 $f = self::fill_field( $field, $form_id );
427
428 self::set_default_value( $f );
429 self::maybe_add_required( $f );
430 self::maybe_update_in_section_variable( $in_section, $f );
431 self::maybe_update_form_select( $f, $imported );
432 self::maybe_update_get_values_form_setting( $imported, $f );
433 self::migrate_placeholders( $f );
434
435 if ( ! empty( $this_form ) ) {
436 // check for field to edit by field id
437 if ( isset( $form_fields[ $f['id'] ] ) ) {
438 FrmField::update( $f['id'], $f );
439 ++$imported['updated']['fields'];
440
441 unset( $form_fields[ $f['id'] ] );
442
443 // Unset old field key.
444 if ( isset( $form_fields[ $f['field_key'] ] ) ) {
445 unset( $form_fields[ $f['field_key'] ] );
446 }
447 } elseif ( isset( $form_fields[ $f['field_key'] ] ) ) {
448 $keys_by_original_field_id[ $f['id'] ] = $f['field_key'];
449
450 $old_field_id = $f['id'];
451
452 // check for field to edit by field key
453 unset( $f['id'] );
454
455 FrmField::update( $form_fields[ $f['field_key'] ], $f );
456 ++$imported['updated']['fields'];
457
458 self::do_after_field_imported_action( $f, $form_fields, $old_field_id );
459
460 // Unset old field id.
461 unset( $form_fields[ $form_fields[ $f['field_key'] ] ] );
462
463 // Unset old field key.
464 unset( $form_fields[ $f['field_key'] ] );
465 } else {
466 // If no matching field id or key in this form, create the field.
467 self::create_imported_field( $f, $imported );
468 }//end if
469 } else {
470
471 self::create_imported_field( $f, $imported );
472 }//end if
473 }//end foreach
474
475 if ( $keys_by_original_field_id ) {
476 self::maybe_update_field_ids( $form_id, $keys_by_original_field_id );
477 }
478 }
479
480 /**
481 * @since 6.8.4
482 *
483 * @param array $field_array
484 * @return array
485 */
486 private static function update_field_options_with_defaults( $field_array ) {
487 $defaults = self::default_field_options( $field_array['type'] );
488 $field_array['field_options'] = array_merge( $defaults, $field_array['field_options'] );
489
490 return $field_array;
491 }
492
493 /**
494 * @since 6.8.4
495 *
496 * @param array $field_array
497 * @param array $form_fields
498 * @param int $old_field_id
499 *
500 * @return void
501 */
502 private static function do_after_field_imported_action( $field_array, $form_fields, $old_field_id ) {
503 // Assign field array the update field's ID.
504 $field_array['id'] = $form_fields[ $field_array['field_key'] ];
505
506 /**
507 * Fires when an existing field is imported.
508 *
509 * @since 6.8.4
510 *
511 * @param array $field_array
512 * @param int $field_id
513 * @param int $old_field_id
514 */
515 do_action( 'frm_after_existing_field_is_imported', $field_array, $form_fields[ $field_array['field_key'] ], $old_field_id );
516 }
517
518 private static function fill_field( $field, $form_id ) {
519 return array(
520 'id' => (int) $field->id,
521 'field_key' => (string) $field->field_key,
522 'name' => (string) $field->name,
523 'description' => (string) $field->description,
524 'type' => (string) $field->type,
525 'default_value' => FrmAppHelper::maybe_json_decode( (string) $field->default_value ),
526 'field_order' => (int) $field->field_order,
527 'form_id' => (int) $form_id,
528 'required' => (int) $field->required,
529 'options' => FrmAppHelper::maybe_json_decode( (string) $field->options ),
530 'field_options' => FrmAppHelper::maybe_json_decode( (string) $field->field_options ),
531 );
532 }
533
534 /**
535 * @since 4.06
536 */
537 private static function set_default_value( &$f ) {
538 $has_default = array(
539 'text',
540 'email',
541 'url',
542 'textarea',
543 'number',
544 'phone',
545 'date',
546 'hidden',
547 'password',
548 'tag',
549 );
550
551 if ( is_array( $f['default_value'] ) && in_array( $f['type'], $has_default, true ) ) {
552 if ( count( $f['default_value'] ) === 1 ) {
553 $f['default_value'] = '[' . reset( $f['default_value'] ) . ']';
554 } else {
555 $f['default_value'] = reset( $f['default_value'] );
556 }
557 }
558 }
559
560 /**
561 * Make sure the required indicator is set.
562 *
563 * @since 4.05
564 */
565 private static function maybe_add_required( &$f ) {
566 if ( $f['required'] && ! isset( $f['field_options']['required_indicator'] ) ) {
567 $f['field_options']['required_indicator'] = '*';
568 }
569 }
570
571 /**
572 * Update the current in_section value at the beginning of the field loop
573 *
574 * @since 2.0.25
575 * @param int $in_section
576 * @param array $f
577 */
578 private static function maybe_update_in_section_variable( &$in_section, &$f ) {
579 // If we're at the end of a section, switch $in_section is 0
580 if ( in_array( $f['type'], array( 'end_divider', 'break', 'form' ) ) ) {
581 $in_section = 0;
582 }
583
584 // Update the current field's in_section value
585 if ( ! isset( $f['field_options']['in_section'] ) ) {
586 $f['field_options']['in_section'] = $in_section;
587 }
588
589 // If we're starting a new section, switch $in_section to ID of divider
590 if ( $f['type'] === 'divider' ) {
591 $in_section = $f['id'];
592 }
593 }
594
595 /**
596 * Switch the form_select on a repeating field or embedded form if it needs to be switched
597 *
598 * @since 2.0.16
599 *
600 * @param array $f
601 * @param array $imported
602 */
603 private static function maybe_update_form_select( &$f, $imported ) {
604 if ( ! isset( $imported['forms'] ) ) {
605 return;
606 }
607
608 if ( $f['type'] === 'form' || ( $f['type'] === 'divider' && FrmField::is_option_true( $f['field_options'], 'repeat' ) ) ) {
609 if ( FrmField::is_option_true( $f['field_options'], 'form_select' ) ) {
610 $form_select = (int) $f['field_options']['form_select'];
611 if ( isset( $imported['forms'][ $form_select ] ) ) {
612 $f['field_options']['form_select'] = $imported['forms'][ $form_select ];
613 }
614 }
615 }
616 }
617
618 /**
619 * Update the get_values_form setting if the form was imported
620 *
621 * @since 2.01.0
622 *
623 * @param array $imported
624 * @param array $f
625 */
626 private static function maybe_update_get_values_form_setting( $imported, &$f ) {
627 if ( ! isset( $imported['forms'] ) ) {
628 return;
629 }
630
631 if ( FrmField::is_option_true_in_array( $f['field_options'], 'get_values_form' ) ) {
632 $old_form = $f['field_options']['get_values_form'];
633 if ( isset( $imported['forms'][ $old_form ] ) ) {
634 $f['field_options']['get_values_form'] = $imported['forms'][ $old_form ];
635 }
636 }
637 }
638
639 /**
640 * If field settings have been migrated, update the values during import.
641 *
642 * @since 4.0
643 */
644 private static function run_field_migrations( &$f ) {
645 self::migrate_placeholders( $f );
646 $f = apply_filters( 'frm_import_xml_field', $f );
647 }
648
649 /**
650 * @since 4.0
651 */
652 private static function migrate_placeholders( &$f ) {
653 $update_values = self::migrate_field_placeholder( $f, 'clear_on_focus' );
654 foreach ( $update_values as $k => $v ) {
655 $f[ $k ] = $v;
656 }
657
658 $update_values = self::migrate_field_placeholder( $f, 'default_blank' );
659 foreach ( $update_values as $k => $v ) {
660 $f[ $k ] = $v;
661 }
662 }
663
664 /**
665 * Move clear_on_focus or default_blank to placeholder.
666 * Also called during database migration in FrmMigrate.
667 *
668 * @since 4.0
669 * @return array
670 */
671 public static function migrate_field_placeholder( $field, $type ) {
672 $field = (array) $field;
673 $field_options = $field['field_options'];
674 if ( empty( $field_options[ $type ] ) || empty( $field['default_value'] ) ) {
675 return array();
676 }
677
678 $field_options['placeholder'] = is_array( $field['default_value'] ) ? reset( $field['default_value'] ) : $field['default_value'];
679 unset( $field_options['default_blank'], $field_options['clear_on_focus'] );
680
681 $changes = array(
682 'field_options' => $field_options,
683 'default_value' => '',
684 );
685
686 // If a dropdown placeholder was used, remove the option so it won't be included twice.
687 $options = $field['options'];
688 if ( $type === 'default_blank' && is_array( $options ) ) {
689 $default_value = $field['default_value'];
690 if ( is_array( $default_value ) ) {
691 $default_value = reset( $default_value );
692 }
693
694 foreach ( $options as $opt_key => $opt ) {
695 if ( is_array( $opt ) ) {
696 $opt = isset( $opt['value'] ) ? $opt['value'] : ( isset( $opt['label'] ) ? $opt['label'] : reset( $opt ) );
697 }
698
699 if ( $opt == $default_value ) {
700 unset( $options[ $opt_key ] );
701 break;
702 }
703 }
704 $changes['options'] = $options;
705 }
706
707 return $changes;
708 }
709
710 /**
711 * Create an imported field
712 *
713 * @since 2.0.25
714 *
715 * @param array $f
716 * @param array $imported
717 */
718 private static function create_imported_field( $f, &$imported ) {
719 $f = self::update_field_options_with_defaults( $f );
720
721 if ( is_callable( 'FrmProFileImport::import_attachment' ) ) {
722 $f = self::maybe_import_images_for_options( $f );
723 }
724
725 $new_id = FrmField::create( $f );
726 if ( $new_id != false ) {
727 ++$imported['imported']['fields'];
728 do_action( 'frm_after_field_is_imported', $f, $new_id );
729 }
730 }
731
732 /**
733 * Import images for radio buttons and checkboxes from image src if available.
734 *
735 * @since 5.5.1
736 *
737 * @param array $field
738 * @return array
739 */
740 private static function maybe_import_images_for_options( $field ) {
741 if ( empty( $field['options'] ) || ! is_array( $field['options'] ) ) {
742 return $field;
743 }
744
745 foreach ( $field['options'] as $key => $option ) {
746 if ( ! is_array( $option ) || empty( $option['src'] ) ) {
747 continue;
748 }
749
750 $field_object = (object) $field;
751 // Fake the file type as FrmProImport::import_attachment checks for file type.
752 $field_object->type = 'file';
753
754 $image_id = FrmProFileImport::import_attachment( $option['src'], $field_object );
755 // Remove the src from options as it isn't required after import.
756 unset( $field['options'][ $key ]['src'] );
757
758 if ( is_numeric( $image_id ) ) {
759 $field['options'][ $key ]['image'] = $image_id;
760 }
761 }
762
763 return $field;
764 }
765
766 /**
767 * Fix field ids for fields that already exist prior to import.
768 *
769 * @since 4.07
770 * @param int $form_id
771 * @param array $keys_by_original_field_id
772 */
773 protected static function maybe_update_field_ids( $form_id, $keys_by_original_field_id ) {
774 global $frm_duplicate_ids;
775
776 $former_duplicate_ids = $frm_duplicate_ids;
777 $where = array(
778 array(
779 'or' => 1,
780 'fi.form_id' => $form_id,
781 'fr.parent_form_id' => $form_id,
782 ),
783 );
784 $fields = FrmField::getAll( $where, 'field_order' );
785 $field_id_by_key = wp_list_pluck( $fields, 'id', 'field_key' );
786
787 foreach ( $fields as $field ) {
788 $before = (array) clone $field;
789 $field = (array) $field;
790 $frm_duplicate_ids = $keys_by_original_field_id;
791 $after = FrmFieldsHelper::switch_field_ids( $field );
792
793 if ( $before['field_options'] !== $after['field_options'] ) {
794 $frm_duplicate_ids = $field_id_by_key;
795 $after = FrmFieldsHelper::switch_field_ids( $after );
796
797 if ( $before['field_options'] !== $after['field_options'] ) {
798 FrmField::update( $field['id'], array( 'field_options' => $after['field_options'] ) );
799 }
800 }
801 }
802
803 $frm_duplicate_ids = $former_duplicate_ids;
804 }
805
806 /**
807 * Updates the custom style setting on import
808 * Convert the post slug to an ID
809 *
810 * @since 2.0.19
811 *
812 * @param array $form
813 */
814 private static function update_custom_style_setting_on_import( &$form ) {
815 if ( ! isset( $form['options']['custom_style'] ) ) {
816 return;
817 }
818
819 if ( is_numeric( $form['options']['custom_style'] ) && 1 === intval( $form['options']['custom_style'] ) ) {
820 // Set to default
821 $form['options']['custom_style'] = 1;
822 } else {
823 // Replace the style name with the style ID on import
824 global $wpdb;
825 $table = $wpdb->prefix . 'posts';
826 $where = array(
827 'post_name' => $form['options']['custom_style'],
828 'post_type' => 'frm_styles',
829 );
830 $select = 'ID';
831 $style_id = FrmDb::get_var( $table, $where, $select );
832
833 if ( $style_id ) {
834 $form['options']['custom_style'] = $style_id;
835 } else {
836 // save the old style to maybe update after styles import
837 $form['options']['old_style'] = $form['options']['custom_style'];
838
839 // Set to default
840 $form['options']['custom_style'] = 1;
841 }
842 }//end if
843 }
844
845 /**
846 * After styles are imported, check for any forms that were linked
847 * and link them back up.
848 *
849 * @since 2.2.7
850 */
851 private static function update_custom_style_setting_after_import( $form_id ) {
852 $form = FrmForm::getOne( $form_id );
853
854 if ( $form && isset( $form->options['old_style'] ) ) {
855 $form = (array) $form;
856 $saved_style = $form['options']['custom_style'];
857 $form['options']['custom_style'] = $form['options']['old_style'];
858 self::update_custom_style_setting_on_import( $form );
859 $has_changed = ( $form['options']['custom_style'] != $saved_style && $form['options']['custom_style'] != $form['options']['old_style'] );
860 if ( $has_changed ) {
861 FrmForm::update( $form['id'], $form );
862 }
863 }
864 }
865
866 public static function import_xml_views( $views, $imported ) {
867 $imported['posts'] = array();
868 $form_action_type = FrmFormActionsController::$action_post_type;
869
870 $post_types = array(
871 'frm_display' => 'views',
872 $form_action_type => 'actions',
873 'frm_styles' => 'styles',
874 );
875
876 $view_ids = array();
877 $posts_with_shortcodes = array();
878
879 foreach ( $views as $item ) {
880 $post = array(
881 'post_title' => (string) $item->title,
882 'post_name' => (string) $item->post_name,
883 'post_type' => (string) $item->post_type,
884 'post_password' => (string) $item->post_password,
885 'guid' => (string) $item->guid,
886 'post_status' => (string) $item->status,
887 'post_author' => FrmAppHelper::get_user_id_param( (string) $item->post_author ),
888 'post_id' => (int) $item->post_id,
889 'post_parent' => (int) $item->post_parent,
890 'menu_order' => (int) $item->menu_order,
891 'post_content' => FrmFieldsHelper::switch_field_ids( (string) $item->content ),
892 'post_excerpt' => FrmFieldsHelper::switch_field_ids( (string) $item->excerpt ),
893 'is_sticky' => (string) $item->is_sticky,
894 'comment_status' => (string) $item->comment_status,
895 'post_date' => (string) $item->post_date,
896 'post_date_gmt' => (string) $item->post_date_gmt,
897 'ping_status' => (string) $item->ping_status,
898 'postmeta' => array(),
899 'layout' => array(),
900 'tax_input' => array(),
901 );
902
903 $post['post_content'] = self::switch_form_ids( $post['post_content'], $imported['forms'] );
904
905 $old_id = $post['post_id'];
906 self::populate_post( $post, $item, $imported );
907
908 unset( $item );
909
910 $post_id = false;
911 if ( $post['post_type'] === $form_action_type ) {
912 $action_control = FrmFormActionsController::get_form_actions( $post['post_excerpt'] );
913 if ( $action_control && is_object( $action_control ) ) {
914 $post_id = $action_control->maybe_create_action( $post, $imported['form_status'] );
915 }
916 unset( $action_control );
917 } elseif ( $post['post_type'] === 'frm_styles' ) {
918 // Properly encode post content before inserting the post
919 $post['post_content'] = FrmAppHelper::maybe_json_decode( $post['post_content'] );
920 $post['post_content'] = FrmAppHelper::prepare_and_encode( $post['post_content'] );
921
922 // Create/update post now
923 $post_id = wp_insert_post( $post );
924 } else {
925 if ( $post['post_type'] === 'frm_display' ) {
926 $post['post_content'] = self::maybe_prepare_json_view_content( $post['post_content'] );
927 } elseif ( 'page' === $post['post_type'] && isset( $imported['posts'][ $post['post_parent'] ] ) ) {
928 $post['post_parent'] = $imported['posts'][ $post['post_parent'] ];
929 }
930 // Create/update post now
931 $post_id = wp_insert_post( $post );
932 }//end if
933
934 if ( ! is_numeric( $post_id ) ) {
935 continue;
936 }
937
938 if ( false !== strpos( $post['post_content'], '[display-frm-data' ) || false !== strpos( $post['post_content'], '[formidable' ) ) {
939 $posts_with_shortcodes[ $post_id ] = $post;
940 }
941
942 self::update_postmeta( $post, $post_id );
943 self::update_layout( $post, $post_id );
944
945 $this_type = 'posts';
946 if ( isset( $post_types[ $post['post_type'] ] ) ) {
947 $this_type = $post_types[ $post['post_type'] ];
948 }
949
950 if ( isset( $post['ID'] ) && $post_id == $post['ID'] ) {
951 ++$imported['updated'][ $this_type ];
952 } else {
953 ++$imported['imported'][ $this_type ];
954 }
955
956 $imported['posts'][ $old_id ] = $post_id;
957
958 if ( $post['post_type'] === 'frm_display' ) {
959 $view_ids[ $old_id ] = $post_id;
960 }
961
962 do_action( 'frm_after_import_view', $post_id, $post );
963
964 unset( $post );
965 }//end foreach
966
967 if ( $posts_with_shortcodes && $view_ids ) {
968 self::maybe_switch_view_ids_after_importing_posts( $posts_with_shortcodes, $view_ids );
969 }
970 unset( $posts_with_shortcodes, $view_ids );
971
972 if ( ! empty( $imported['forms'] ) ) {
973 // clear imported forms style cache to make sure the new styles are applied to the forms
974 self::clear_forms_style_caches( $imported['forms'] );
975 }
976
977 self::maybe_update_stylesheet( $imported );
978
979 flush_rewrite_rules();
980
981 return $imported;
982 }
983
984 /**
985 * Clears styles from cache for imported forms
986 *
987 * @param array $imported_forms
988 */
989 private static function clear_forms_style_caches( $imported_forms ) {
990 $where = array(
991 'id' => $imported_forms,
992 'options LIKE' => '"old_style"',
993 );
994 $forms = FrmDb::get_results( 'frm_forms', $where );
995
996 foreach ( $forms as $form ) {
997 FrmAppHelper::unserialize_or_decode( $form->options );
998 if ( ! $form->options ) {
999 continue;
1000 }
1001 $where = array(
1002 'post_name' => $form->options['old_style'],
1003 'post_type' => FrmStylesController::$post_type,
1004 );
1005
1006 $select = 'ID';
1007
1008 $cache_key = FrmDb::generate_cache_key( $where, array( 'limit' => 1 ), $select, 'var' );
1009 FrmDb::delete_cache_and_transient( $cache_key, 'post' );
1010 }
1011 }
1012
1013 /**
1014 * Replace old form ids with new ones in a string.
1015 *
1016 * @param string $string
1017 * @param array<int> $form_ids new form ids indexed by old form id.
1018 * @return string
1019 */
1020 private static function switch_form_ids( $string, $form_ids ) {
1021 if ( false === strpos( $string, '[formidable' ) ) {
1022 // Skip string replacing if there are no form shortcodes in string.
1023 return $string;
1024 }
1025
1026 foreach ( $form_ids as $old_id => $new_id ) {
1027 $string = str_replace(
1028 array(
1029 '[formidable id="' . $old_id . '"',
1030 '[formidable id=' . $old_id . ']',
1031 '[formidable id=' . $old_id . ' ',
1032 '"formId":"' . $old_id . '"',
1033 ),
1034 array(
1035 '[formidable id="' . $new_id . '"',
1036 '[formidable id=' . $new_id . ']',
1037 '[formidable id=' . $new_id . ' ',
1038 '"formId":"' . $new_id . '"',
1039 ),
1040 $string
1041 );
1042 }
1043
1044 return $string;
1045 }
1046
1047 /**
1048 * @param array<array> $posts_with_shortcodes indexed by current post id.
1049 * @param array<int> $view_ids new view ids indexed by old view id.
1050 * @return void
1051 */
1052 private static function maybe_switch_view_ids_after_importing_posts( $posts_with_shortcodes, $view_ids ) {
1053 foreach ( $posts_with_shortcodes as $imported_post_id => $post ) {
1054 $post_content = self::switch_view_ids( $post['post_content'], $view_ids );
1055 if ( $post_content === $post['post_content'] ) {
1056 continue;
1057 }
1058
1059 wp_update_post(
1060 array(
1061 'ID' => $imported_post_id,
1062 'post_content' => $post_content,
1063 )
1064 );
1065 }
1066 }
1067
1068 /**
1069 * Replace old view ids with new ones in a string.
1070 *
1071 * @param string $string
1072 * @param array<int> $view_ids new view ids indexed by old view id.
1073 * @return string
1074 */
1075 private static function switch_view_ids( $string, $view_ids ) {
1076 if ( false === strpos( $string, '[display-frm-data' ) ) {
1077 // Skip string replacing if there are no view shortcodes in string.
1078 return $string;
1079 }
1080
1081 foreach ( $view_ids as $old_id => $new_id ) {
1082 $string = str_replace(
1083 array(
1084 '[display-frm-data id="' . $old_id . '"',
1085 '[display-frm-data id=' . $old_id . ']',
1086 '[display-frm-data id=' . $old_id . ' ',
1087 '"viewId":"' . $old_id . '"',
1088 ),
1089 array(
1090 '[display-frm-data id="' . $new_id . '"',
1091 '[display-frm-data id=' . $new_id . ']',
1092 '[display-frm-data id=' . $new_id . ' ',
1093 '"viewId":"' . $new_id . '"',
1094 ),
1095 $string
1096 );
1097 unset( $old_id, $new_id );
1098 }
1099
1100 return $string;
1101 }
1102
1103 /**
1104 * @param string $content
1105 * @return string
1106 */
1107 private static function maybe_prepare_json_view_content( $content ) {
1108 $maybe_decoded = FrmAppHelper::maybe_json_decode( $content );
1109 if ( is_array( $maybe_decoded ) && isset( $maybe_decoded[0] ) && isset( $maybe_decoded[0]['box'] ) ) {
1110 return FrmAppHelper::prepare_and_encode( $maybe_decoded );
1111 }
1112 return $content;
1113 }
1114
1115 private static function populate_post( &$post, $item, $imported ) {
1116 if ( isset( $item->attachment_url ) ) {
1117 $post['attachment_url'] = (string) $item->attachment_url;
1118 }
1119
1120 if ( $post['post_type'] == FrmFormActionsController::$action_post_type && isset( $imported['forms'][ (int) $post['menu_order'] ] ) ) {
1121 // update to new form id
1122 $post['menu_order'] = $imported['forms'][ (int) $post['menu_order'] ];
1123 }
1124
1125 // Don't allow default styles to take over a site's default style
1126 if ( 'frm_styles' == $post['post_type'] ) {
1127 $post['menu_order'] = 0;
1128 }
1129
1130 foreach ( $item->postmeta as $meta ) {
1131 self::populate_postmeta( $post, $meta, $imported );
1132 unset( $meta );
1133 }
1134
1135 foreach ( $item->layout as $layout ) {
1136 self::populate_layout( $post, $layout );
1137 unset( $layout );
1138 }
1139
1140 self::populate_taxonomies( $post, $item );
1141
1142 self::maybe_editing_post( $post );
1143 }
1144
1145 /**
1146 * @param array $post
1147 * @param stdClass $meta
1148 * @param array $imported
1149 */
1150 private static function populate_postmeta( &$post, $meta, $imported ) {
1151 global $frm_duplicate_ids;
1152
1153 $m = array(
1154 'key' => (string) $meta->meta_key,
1155 'value' => (string) $meta->meta_value,
1156 );
1157
1158 // Switch old form and field ids to new ones.
1159 if ( 'frm_form_id' === $m['key'] && isset( $imported['forms'][ (int) $m['value'] ] ) ) {
1160 $m['value'] = $imported['forms'][ (int) $m['value'] ];
1161 } else {
1162 $m['value'] = FrmAppHelper::maybe_json_decode( $m['value'] );
1163
1164 if ( ! empty( $frm_duplicate_ids ) ) {
1165 if ( 'frm_dyncontent' === $m['key'] ) {
1166 $m['value'] = self::maybe_prepare_json_view_content( $m['value'] );
1167 $m['value'] = FrmFieldsHelper::switch_field_ids( $m['value'] );
1168 } elseif ( 'frm_options' === $m['key'] ) {
1169
1170 foreach ( array( 'date_field_id', 'edate_field_id' ) as $setting_name ) {
1171 if ( isset( $m['value'][ $setting_name ] ) && is_numeric( $m['value'][ $setting_name ] ) && isset( $frm_duplicate_ids[ $m['value'][ $setting_name ] ] ) ) {
1172 $m['value'][ $setting_name ] = $frm_duplicate_ids[ $m['value'][ $setting_name ] ];
1173 }
1174 }
1175
1176 $check_dup_array = array();
1177 if ( ! empty( $m['value']['order_by'] ) ) {
1178 if ( is_numeric( $m['value']['order_by'] ) && isset( $frm_duplicate_ids[ $m['value']['order_by'] ] ) ) {
1179 $m['value']['order_by'] = $frm_duplicate_ids[ $m['value']['order_by'] ];
1180 } elseif ( is_array( $m['value']['order_by'] ) ) {
1181 $check_dup_array[] = 'order_by';
1182 }
1183 }
1184
1185 if ( ! empty( $m['value']['where'] ) ) {
1186 $check_dup_array[] = 'where';
1187 }
1188
1189 foreach ( $check_dup_array as $check_k ) {
1190 foreach ( (array) $m['value'][ $check_k ] as $mk => $mv ) {
1191 if ( isset( $frm_duplicate_ids[ $mv ] ) ) {
1192 $m['value'][ $check_k ][ $mk ] = $frm_duplicate_ids[ $mv ];
1193 }
1194 unset( $mk, $mv );
1195 }
1196 }
1197 }//end if
1198 }//end if
1199 }//end if
1200
1201 if ( ! is_array( $m['value'] ) ) {
1202 $m['value'] = FrmAppHelper::maybe_json_decode( $m['value'] );
1203 }
1204
1205 $post['postmeta'][ (string) $meta->meta_key ] = $m['value'];
1206 }
1207
1208 private static function populate_layout( &$post, $layout ) {
1209 $post['layout'][ (string) $layout->type ] = (string) $layout->data;
1210 }
1211
1212 /**
1213 * Add terms to post
1214 *
1215 * @param array $post By reference.
1216 * @param object $item The XML object data.
1217 */
1218 private static function populate_taxonomies( &$post, $item ) {
1219 foreach ( $item->category as $c ) {
1220 $att = $c->attributes();
1221 if ( ! isset( $att['nicename'] ) ) {
1222 continue;
1223 }
1224
1225 $taxonomy = (string) $att['domain'];
1226 if ( is_taxonomy_hierarchical( $taxonomy ) ) {
1227 $name = (string) $att['nicename'];
1228 $h_term = get_term_by( 'slug', $name, $taxonomy );
1229 if ( $h_term ) {
1230 $name = $h_term->term_id;
1231 }
1232 unset( $h_term );
1233 } else {
1234 $name = (string) $c;
1235 }
1236
1237 if ( ! isset( $post['tax_input'][ $taxonomy ] ) ) {
1238 $post['tax_input'][ $taxonomy ] = array();
1239 }
1240
1241 $post['tax_input'][ $taxonomy ][] = $name;
1242 unset( $name );
1243 }//end foreach
1244 }
1245
1246 /**
1247 * Edit post if the key and created time match
1248 */
1249 private static function maybe_editing_post( &$post ) {
1250 $match_by = array(
1251 'post_type' => $post['post_type'],
1252 'name' => $post['post_name'],
1253 'post_status' => $post['post_status'],
1254 'posts_per_page' => 1,
1255 );
1256
1257 if ( in_array( $post['post_status'], array( 'trash', 'draft' ) ) ) {
1258 $match_by['include'] = $post['post_id'];
1259 unset( $match_by['name'] );
1260 }
1261
1262 $editing = get_posts( $match_by );
1263
1264 if ( ! empty( $editing ) && current( $editing )->post_date == $post['post_date'] ) {
1265 // set the id of the post to edit
1266 $post['ID'] = current( $editing )->ID;
1267 }
1268 }
1269
1270 /**
1271 * @param array $post
1272 * @param int $post_id
1273 * @return void
1274 */
1275 private static function update_postmeta( &$post, $post_id ) {
1276 foreach ( $post['postmeta'] as $k => $v ) {
1277 switch ( $k ) {
1278 case '_edit_last':
1279 $v = FrmAppHelper::get_user_id_param( $v );
1280 break;
1281
1282 case '_thumbnail_id':
1283 if ( FrmAppHelper::pro_is_installed() ) {
1284 // Change the attachment ID.
1285 $field_obj = FrmFieldFactory::get_field_type( 'file' );
1286 $v = $field_obj->get_file_id( $v );
1287 }
1288 break;
1289
1290 case 'frm_dyncontent':
1291 if ( is_array( $v ) ) {
1292 $v = json_encode( $v );
1293 }
1294 break;
1295
1296 case 'frm_param':
1297 add_rewrite_endpoint( $v, EP_PERMALINK | EP_PAGES );
1298 break;
1299 }//end switch
1300
1301 update_post_meta( $post_id, $k, $v );
1302 unset( $k, $v );
1303 }//end foreach
1304 }
1305
1306 /**
1307 * @param array $post
1308 * @param int $post_id
1309 */
1310 private static function update_layout( &$post, $post_id ) {
1311 if ( is_callable( 'FrmViewsLayout::maybe_create_layouts_for_view' ) ) {
1312 $listing_layout = ! empty( $post['layout']['listing'] ) ? json_decode( $post['layout']['listing'], true ) : array();
1313 $detail_layout = ! empty( $post['layout']['detail'] ) ? json_decode( $post['layout']['detail'], true ) : array();
1314 if ( $listing_layout || $detail_layout ) {
1315 FrmViewsLayout::maybe_create_layouts_for_view( $post_id, $listing_layout, $detail_layout );
1316 }
1317 }
1318 }
1319
1320 private static function maybe_update_stylesheet( $imported ) {
1321 $new_styles = ! empty( $imported['imported']['styles'] );
1322 $updated_styles = ! empty( $imported['updated']['styles'] );
1323 if ( $new_styles || $updated_styles ) {
1324 if ( is_admin() && function_exists( 'get_filesystem_method' ) ) {
1325 $frm_style = new FrmStyle();
1326 $frm_style->update( 'default' );
1327 }
1328 foreach ( $imported['forms'] as $form_id ) {
1329 self::update_custom_style_setting_after_import( $form_id );
1330 }
1331 }
1332 }
1333
1334 /**
1335 * @param mixed $result
1336 * @param string $message
1337 * @param array $errors
1338 */
1339 public static function parse_message( $result, &$message, &$errors ) {
1340 if ( is_wp_error( $result ) ) {
1341 $errors[] = $result->get_error_message();
1342
1343 // Remove the SimpleXML_parse_error from the WP_Error object to avoid
1344 // displaying duplicate error messages from $result->get_error_message()
1345 $error_codes = $result->get_error_codes();
1346 $error_details = array();
1347 foreach ( $error_codes as $error_code ) {
1348 // Clone WP_Error data because WP_Error removes all error messages and data
1349 // associated with the specified error code when an item is removed.
1350 // Source: https://developer.wordpress.org/reference/classes/wp_error/remove/#source
1351 $error_details = $result->get_error_data( $error_code );
1352 if ( $error_code === 'SimpleXML_parse_error' ) {
1353 $result->remove( $error_code );
1354 break;
1355 }
1356 }
1357
1358 if ( ! empty( $error_details ) ) {
1359 $errors[] = '<br />' . esc_html_x( 'Error details:', 'import xml message', 'formidable' ) . '<br />' . esc_html( print_r( $error_details, 1 ) );
1360 }
1361
1362 return;
1363 }//end if
1364
1365 if ( ! $result ) {
1366 return;
1367 }
1368
1369 if ( ! is_array( $result ) ) {
1370 $message = is_string( $result ) ? $result : htmlentities( print_r( $result, 1 ) );
1371 return;
1372 }
1373
1374 $t_strings = array(
1375 'imported' => __( 'Imported', 'formidable' ),
1376 'updated' => __( 'Updated', 'formidable' ),
1377 );
1378
1379 $message = '<ul>';
1380 foreach ( $result as $type => $results ) {
1381 if ( ! isset( $t_strings[ $type ] ) ) {
1382 // only print imported and updated
1383 continue;
1384 }
1385
1386 $s_message = array();
1387 foreach ( $results as $k => $m ) {
1388 self::item_count_message( $m, $k, $s_message );
1389 unset( $k, $m );
1390 }
1391
1392 if ( ! empty( $s_message ) ) {
1393 $message .= '<li><strong>' . $t_strings[ $type ] . ':</strong> ';
1394 $message .= implode( ', ', $s_message );
1395 $message .= '</li>';
1396 }
1397 }
1398
1399 if ( $message === '<ul>' ) {
1400 $message = '';
1401 $errors[] = __( 'Nothing was imported or updated', 'formidable' );
1402 } else {
1403 self::add_form_link_to_message( $result, $message );
1404
1405 /**
1406 * @since 5.3
1407 *
1408 * @param string $message
1409 * @param array $result
1410 */
1411 $message = apply_filters( 'frm_xml_parsed_message', $message, $result );
1412 $message .= '</ul>';
1413 }
1414 }
1415
1416 /**
1417 * @param int $m
1418 * @param string $type
1419 * @param array<string> $s_message
1420 */
1421 public static function item_count_message( $m, $type, &$s_message ) {
1422 if ( ! $m ) {
1423 return;
1424 }
1425
1426 $strings = array(
1427 /* translators: %1$s: Number of items */
1428 'forms' => sprintf( _n( '%1$s Form', '%1$s Forms', $m, 'formidable' ), $m ),
1429 /* translators: %1$s: Number of items */
1430 'fields' => sprintf( _n( '%1$s Field', '%1$s Fields', $m, 'formidable' ), $m ),
1431 /* translators: %1$s: Number of items */
1432 'items' => sprintf( _n( '%1$s Entry', '%1$s Entries', $m, 'formidable' ), $m ),
1433 /* translators: %1$s: Number of items */
1434 'views' => sprintf( _n( '%1$s View', '%1$s Views', $m, 'formidable' ), $m ),
1435 /* translators: %1$s: Number of items */
1436 'posts' => sprintf( _n( '%1$s Page/Post', '%1$s Pages/Posts', $m, 'formidable' ), $m ),
1437 /* translators: %1$s: Number of items */
1438 'styles' => sprintf( _n( '%1$s Style', '%1$s Styles', $m, 'formidable' ), $m ),
1439 /* translators: %1$s: Number of items */
1440 'terms' => sprintf( _n( '%1$s Term', '%1$s Terms', $m, 'formidable' ), $m ),
1441 /* translators: %1$s: Number of items */
1442 'actions' => sprintf( _n( '%1$s Form Action', '%1$s Form Actions', $m, 'formidable' ), $m ),
1443 );
1444
1445 if ( isset( $strings[ $type ] ) ) {
1446 $s_message[] = $strings[ $type ];
1447 } else {
1448 $string = ' ' . $m . ' ' . ucfirst( $type );
1449
1450 /**
1451 * @since 5.3
1452 *
1453 * @param string $string Message string for imported item.
1454 * @param int $m Number of item that was imported.
1455 * }
1456 */
1457 $string = apply_filters( 'frm_xml_' . $type . '_count_message', $string, $m );
1458 $s_message[] = $string;
1459 }
1460 }
1461
1462 /**
1463 * If a single form was imported, include a link in the success message.
1464 *
1465 * @since 4.0
1466 * @param array $result The response from the XML import.
1467 * @param string $message The response shown on the page after import.
1468 */
1469 private static function add_form_link_to_message( $result, &$message ) {
1470 $total_forms = $result['imported']['forms'] + $result['updated']['forms'];
1471 if ( $total_forms > 1 ) {
1472 return;
1473 }
1474
1475 $primary_form = reset( $result['forms'] );
1476 if ( ! empty( $primary_form ) ) {
1477 $primary_form = FrmForm::getOne( $primary_form );
1478 $form_id = empty( $primary_form->parent_form_id ) ? $primary_form->id : $primary_form->parent_form_id;
1479
1480 $message .= '<li><a href="' . esc_url( FrmForm::get_edit_link( $form_id ) ) . '">' . esc_html__( 'Go to imported form', 'formidable' ) . '</a></li>';
1481 }
1482 }
1483
1484 /**
1485 * Prepare the form options for export
1486 *
1487 * @since 2.0.19
1488 *
1489 * @param string $options
1490 *
1491 * @return string
1492 */
1493 public static function prepare_form_options_for_export( $options ) {
1494 FrmAppHelper::unserialize_or_decode( $options );
1495 // Change custom_style to the post_name instead of ID (1 may be a string)
1496 $not_default = isset( $options['custom_style'] ) && 1 != $options['custom_style'];
1497 if ( $not_default ) {
1498 global $wpdb;
1499 $table = $wpdb->prefix . 'posts';
1500 $where = array( 'ID' => $options['custom_style'] );
1501 $select = 'post_name';
1502
1503 $style_name = FrmDb::get_var( $table, $where, $select );
1504
1505 if ( $style_name ) {
1506 $options['custom_style'] = $style_name;
1507 } else {
1508 $options['custom_style'] = 1;
1509 }
1510 }
1511 self::remove_default_form_options( $options );
1512 $options = serialize( $options );
1513
1514 return self::cdata( $options );
1515 }
1516
1517 /**
1518 * If the saved value is the same as the default, remove it from the export
1519 * This keeps file size down and prevents overriding global settings after import
1520 *
1521 * @since 3.06
1522 */
1523 private static function remove_default_form_options( &$options ) {
1524 $defaults = FrmFormsHelper::get_default_opts();
1525 if ( is_callable( 'FrmProFormsHelper::get_default_opts' ) ) {
1526 $defaults += FrmProFormsHelper::get_default_opts();
1527 }
1528 self::remove_defaults( $defaults, $options );
1529 }
1530
1531 /**
1532 * Remove extra settings from field to keep file size down
1533 *
1534 * @since 3.06
1535 */
1536 public static function prepare_field_for_export( &$field ) {
1537 self::remove_default_field_options( $field );
1538 self::add_image_src_to_image_options( $field );
1539 }
1540
1541 /**
1542 * Remove defaults from field options too
1543 *
1544 * @since 3.06
1545 */
1546 private static function remove_default_field_options( &$field ) {
1547 $defaults = self::default_field_options( $field->type );
1548 if ( empty( $defaults['blank'] ) ) {
1549 $global_settings = new FrmSettings();
1550 $global_defaults = $global_settings->default_options();
1551 $defaults['blank'] = $global_defaults['blank_msg'];
1552 }
1553
1554 $options = $field->field_options;
1555 FrmAppHelper::unserialize_or_decode( $options );
1556 self::remove_defaults( $defaults, $options );
1557 self::remove_default_html( 'custom_html', $defaults, $options );
1558
1559 // Get variations on the defaults.
1560 if ( isset( $options['invalid'] ) ) {
1561 $defaults = array(
1562 /* translators: %s: Field name */
1563 'invalid' => sprintf( __( '%s is invalid', 'formidable' ), $field->name ),
1564 );
1565 self::remove_defaults( $defaults, $options );
1566 }
1567
1568 $field->field_options = serialize( $options );
1569 }
1570
1571 /**
1572 * Add image "src" key to each image option so the image can be imported to another website.
1573 *
1574 * @since 5.5.1
1575 *
1576 * @param stdClass $field
1577 * @return void
1578 */
1579 private static function add_image_src_to_image_options( $field ) {
1580 if ( empty( $field->options ) || false === strpos( $field->options, 'image' ) ) {
1581 return;
1582 }
1583
1584 $updated = false;
1585 $options = $field->options;
1586 FrmAppHelper::unserialize_or_decode( $options );
1587
1588 if ( ! $options || ! is_array( $options ) ) {
1589 return;
1590 }
1591
1592 foreach ( $options as $key => $option ) {
1593 if ( is_array( $option ) && ! empty( $option['image'] ) ) {
1594 $options[ $key ]['src'] = wp_get_attachment_url( $option['image'] );
1595 $updated = true;
1596 }
1597 }
1598
1599 if ( $updated ) {
1600 $field->options = maybe_serialize( $options );
1601 }
1602 }
1603
1604 /**
1605 * @since 3.06.03
1606 */
1607 private static function default_field_options( $type ) {
1608 $defaults = FrmFieldsHelper::get_default_field_options( $type );
1609 if ( empty( $defaults['custom_html'] ) ) {
1610 $defaults['custom_html'] = FrmFieldsHelper::get_default_html( $type );
1611 }
1612 return $defaults;
1613 }
1614
1615 /**
1616 * Compare the default array to the saved values and
1617 * remove if they are the same
1618 *
1619 * @since 3.06
1620 */
1621 private static function remove_defaults( $defaults, &$saved ) {
1622 foreach ( $saved as $key => $value ) {
1623 if ( isset( $defaults[ $key ] ) && $defaults[ $key ] === $value ) {
1624 unset( $saved[ $key ] );
1625 }
1626 }
1627 }
1628
1629 /**
1630 * The line endings may prevent html from being equal when it should
1631 *
1632 * @since 3.06
1633 */
1634 private static function remove_default_html( $html_name, $defaults, &$options ) {
1635 if ( ! isset( $options[ $html_name ] ) || ! isset( $defaults[ $html_name ] ) ) {
1636 return;
1637 }
1638
1639 $old_html = str_replace( "\r\n", "\n", $options[ $html_name ] );
1640 $default_html = $defaults[ $html_name ];
1641 if ( $old_html == $default_html ) {
1642 unset( $options[ $html_name ] );
1643
1644 return;
1645 }
1646
1647 // Account for some of the older field default HTML.
1648 $default_html = str_replace( ' id="frm_desc_field_[key]"', '', $default_html );
1649 if ( $old_html == $default_html ) {
1650 unset( $options[ $html_name ] );
1651 }
1652 }
1653
1654 public static function cdata( $str ) {
1655 FrmAppHelper::unserialize_or_decode( $str );
1656 if ( is_array( $str ) ) {
1657 $str = json_encode( $str );
1658 } elseif ( seems_utf8( $str ) === false ) {
1659 $str = FrmAppHelper::maybe_utf8_encode( $str );
1660 }
1661
1662 if ( is_numeric( $str ) ) {
1663 return $str;
1664 }
1665
1666 self::remove_invalid_characters_from_xml( $str );
1667
1668 // $str = ent2ncr(esc_html( $str));
1669 $str = '<![CDATA[' . str_replace( ']]>', ']]]]><![CDATA[>', $str ) . ']]>';
1670
1671 return $str;
1672 }
1673
1674 /**
1675 * Remove <US> character (unit separator) from exported strings
1676 *
1677 * @since 2.0.22
1678 *
1679 * @param string $str
1680 */
1681 private static function remove_invalid_characters_from_xml( &$str ) {
1682 // Remove <US> character
1683 $str = str_replace( '\x1F', '', $str );
1684 }
1685
1686 public static function migrate_form_settings_to_actions( $form_options, $form_id, &$imported = array(), $switch = false ) {
1687 // Get post type
1688 $post_type = FrmFormActionsController::$action_post_type;
1689
1690 // Set up imported index, if not set up yet
1691 if ( ! isset( $imported['imported']['actions'] ) ) {
1692 $imported['imported']['actions'] = 0;
1693 }
1694
1695 // Migrate post settings to action
1696 self::migrate_post_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1697
1698 // Migrate email settings to action
1699 self::migrate_email_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1700 }
1701
1702 /**
1703 * Migrate post settings to form action
1704 *
1705 * @param array $form_options
1706 * @param int $form_id
1707 * @param string $post_type
1708 * @param array $imported
1709 * @param bool $switch
1710 */
1711 private static function migrate_post_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1712 if ( ! isset( $form_options['create_post'] ) || ! $form_options['create_post'] ) {
1713 return;
1714 }
1715
1716 $new_action = array(
1717 'post_type' => $post_type,
1718 'post_excerpt' => 'wppost',
1719 'post_title' => __( 'Create Posts', 'formidable' ),
1720 'menu_order' => $form_id,
1721 'post_status' => 'publish',
1722 'post_content' => array(),
1723 'post_name' => $form_id . '_wppost_1',
1724 );
1725
1726 $post_settings = array(
1727 'post_type',
1728 'post_category',
1729 'post_content',
1730 'post_excerpt',
1731 'post_title',
1732 'post_name',
1733 'post_date',
1734 'post_status',
1735 'post_custom_fields',
1736 'post_password',
1737 'post_parent',
1738 );
1739
1740 foreach ( $post_settings as $post_setting ) {
1741 if ( isset( $form_options[ $post_setting ] ) ) {
1742 $new_action['post_content'][ $post_setting ] = $form_options[ $post_setting ];
1743 }
1744 unset( $post_setting );
1745 }
1746
1747 $new_action['event'] = array( 'create', 'update' );
1748
1749 if ( $switch ) {
1750 // Fields with string or int saved.
1751 $basic_fields = array(
1752 'post_title',
1753 'post_content',
1754 'post_excerpt',
1755 'post_password',
1756 'post_date',
1757 'post_status',
1758 'post_parent',
1759 );
1760
1761 // Fields with arrays saved.
1762 $array_fields = array( 'post_category', 'post_custom_fields' );
1763
1764 $new_action['post_content'] = self::switch_action_field_ids( $new_action['post_content'], $basic_fields, $array_fields );
1765 }
1766 $new_action['post_content'] = json_encode( $new_action['post_content'] );
1767
1768 $exists = get_posts(
1769 array(
1770 'name' => $new_action['post_name'],
1771 'post_type' => $new_action['post_type'],
1772 'post_status' => $new_action['post_status'],
1773 'numberposts' => 1,
1774 )
1775 );
1776
1777 if ( ! $exists ) {
1778 // this isn't an email, but we need to use a class that will always be included
1779 FrmDb::save_json_post( $new_action );
1780 ++$imported['imported']['actions'];
1781 }
1782 }
1783
1784 /**
1785 * Switch old field IDs for new field IDs in emails and post
1786 *
1787 * @since 2.0
1788 *
1789 * @param array $post_content Check for old field IDs.
1790 * @param array $basic_fields Fields with string or int saved.
1791 * @param array $array_fields Fields with arrays saved.
1792 *
1793 * @return string $post_content - new field IDs
1794 */
1795 private static function switch_action_field_ids( $post_content, $basic_fields, $array_fields = array() ) {
1796 global $frm_duplicate_ids;
1797
1798 // If there aren't IDs that were switched, end now
1799 if ( ! $frm_duplicate_ids ) {
1800 return;
1801 }
1802
1803 // Get old IDs
1804 $old = array_keys( $frm_duplicate_ids );
1805
1806 // Get new IDs
1807 $new = array_values( $frm_duplicate_ids );
1808
1809 // Do a str_replace with each item to set the new IDs
1810 foreach ( $post_content as $key => $setting ) {
1811 if ( ! is_array( $setting ) && in_array( $key, $basic_fields ) ) {
1812 // Replace old IDs with new IDs
1813 $post_content[ $key ] = str_replace( $old, $new, $setting );
1814 } elseif ( is_array( $setting ) && in_array( $key, $array_fields ) ) {
1815 foreach ( $setting as $k => $val ) {
1816 // Replace old IDs with new IDs
1817 $post_content[ $key ][ $k ] = str_replace( $old, $new, $val );
1818 }
1819 }
1820 unset( $key, $setting );
1821 }
1822
1823 return $post_content;
1824 }
1825
1826 private static function migrate_email_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1827 // No old notifications or autoresponders to carry over
1828 if ( ! isset( $form_options['auto_responder'] ) && ! isset( $form_options['notification'] ) && ! isset( $form_options['email_to'] ) ) {
1829 return;
1830 }
1831
1832 // Initialize notifications array
1833 $notifications = array();
1834
1835 // Migrate regular notifications
1836 self::migrate_notifications_to_action( $form_options, $form_id, $notifications );
1837
1838 // Migrate autoresponders
1839 self::migrate_autoresponder_to_action( $form_options, $form_id, $notifications );
1840
1841 if ( empty( $notifications ) ) {
1842 return;
1843 }
1844
1845 foreach ( $notifications as $new_notification ) {
1846 $new_notification['post_type'] = $post_type;
1847 $new_notification['post_excerpt'] = 'email';
1848 $new_notification['post_title'] = __( 'Email Notification', 'formidable' );
1849 $new_notification['menu_order'] = $form_id;
1850 $new_notification['post_status'] = 'publish';
1851
1852 // Switch field IDs and keys, if needed
1853 if ( $switch ) {
1854
1855 // Switch field IDs in email conditional logic
1856 self::switch_email_condition_field_ids( $new_notification['post_content'] );
1857
1858 // Switch all other field IDs in email
1859 $new_notification['post_content'] = FrmFieldsHelper::switch_field_ids( $new_notification['post_content'] );
1860 }
1861 $new_notification['post_content'] = FrmAppHelper::prepare_and_encode( $new_notification['post_content'] );
1862
1863 $exists = get_posts(
1864 array(
1865 'name' => $new_notification['post_name'],
1866 'post_type' => $new_notification['post_type'],
1867 'post_status' => $new_notification['post_status'],
1868 'numberposts' => 1,
1869 )
1870 );
1871
1872 if ( empty( $exists ) ) {
1873 FrmDb::save_json_post( $new_notification );
1874 ++$imported['imported']['actions'];
1875 }
1876 unset( $new_notification );
1877 }//end foreach
1878
1879 self::remove_deprecated_notification_settings( $form_id, $form_options );
1880 }
1881
1882 /**
1883 * Remove deprecated notification settings after migration
1884 *
1885 * @since 2.05
1886 *
1887 * @param int|string $form_id
1888 * @param array $form_options
1889 */
1890 private static function remove_deprecated_notification_settings( $form_id, $form_options ) {
1891 $delete_settings = array( 'notification', 'autoresponder', 'email_to' );
1892 foreach ( $delete_settings as $index ) {
1893 if ( isset( $form_options[ $index ] ) ) {
1894 unset( $form_options[ $index ] );
1895 }
1896 }
1897 FrmForm::update( $form_id, array( 'options' => $form_options ) );
1898 }
1899
1900 private static function migrate_notifications_to_action( $form_options, $form_id, &$notifications ) {
1901 if ( ! isset( $form_options['notification'] ) && ! empty( $form_options['email_to'] ) ) {
1902 // add old settings into notification array
1903 $form_options['notification'] = array( 0 => $form_options );
1904 } elseif ( isset( $form_options['notification']['email_to'] ) ) {
1905 // make sure it's in the correct format
1906 $form_options['notification'] = array( 0 => $form_options['notification'] );
1907 }
1908
1909 if ( isset( $form_options['notification'] ) && is_array( $form_options['notification'] ) ) {
1910 foreach ( $form_options['notification'] as $email_key => $notification ) {
1911
1912 $atts = array(
1913 'email_to' => '',
1914 'reply_to' => '',
1915 'reply_to_name' => '',
1916 'event' => '',
1917 'form_id' => $form_id,
1918 'email_key' => $email_key,
1919 );
1920
1921 // Format the email data
1922 self::format_email_data( $atts, $notification );
1923
1924 if ( isset( $notification['twilio'] ) && $notification['twilio'] ) {
1925 do_action( 'frm_create_twilio_action', $atts, $notification );
1926 }
1927
1928 // Setup the new notification
1929 $new_notification = array();
1930 self::setup_new_notification( $new_notification, $notification, $atts );
1931
1932 $notifications[] = $new_notification;
1933 }//end foreach
1934 }//end if
1935 }
1936
1937 private static function format_email_data( &$atts, $notification ) {
1938 // Format email_to
1939 self::format_email_to_data( $atts, $notification );
1940
1941 // Format the reply to email and name
1942 $reply_fields = array(
1943 'reply_to' => '',
1944 'reply_to_name' => '',
1945 );
1946 foreach ( $reply_fields as $f => $val ) {
1947 if ( isset( $notification[ $f ] ) ) {
1948 $atts[ $f ] = $notification[ $f ];
1949 if ( 'custom' == $notification[ $f ] ) {
1950 $atts[ $f ] = $notification[ 'cust_' . $f ];
1951 } elseif ( is_numeric( $atts[ $f ] ) && ! empty( $atts[ $f ] ) ) {
1952 $atts[ $f ] = '[' . $atts[ $f ] . ']';
1953 }
1954 }
1955 unset( $f, $val );
1956 }
1957
1958 // Format event
1959 $atts['event'] = array( 'create' );
1960 if ( isset( $notification['update_email'] ) && 1 == $notification['update_email'] ) {
1961 $atts['event'][] = 'update';
1962 } elseif ( isset( $notification['update_email'] ) && 2 == $notification['update_email'] ) {
1963 $atts['event'] = array( 'update' );
1964 }
1965 }
1966
1967 private static function format_email_to_data( &$atts, $notification ) {
1968 if ( isset( $notification['email_to'] ) ) {
1969 $atts['email_to'] = preg_split( '/ (,|;) /', $notification['email_to'] );
1970 } else {
1971 $atts['email_to'] = array();
1972 }
1973
1974 if ( isset( $notification['also_email_to'] ) ) {
1975 $email_fields = (array) $notification['also_email_to'];
1976 $atts['email_to'] = array_merge( $email_fields, $atts['email_to'] );
1977 unset( $email_fields );
1978 }
1979
1980 foreach ( $atts['email_to'] as $key => $email_field ) {
1981
1982 if ( is_numeric( $email_field ) ) {
1983 $atts['email_to'][ $key ] = '[' . $email_field . ']';
1984 }
1985
1986 if ( strpos( $email_field, '|' ) ) {
1987 $email_opt = explode( '|', $email_field );
1988 if ( isset( $email_opt[0] ) ) {
1989 $atts['email_to'][ $key ] = '[' . $email_opt[0] . ' show=' . $email_opt[1] . ']';
1990 }
1991 unset( $email_opt );
1992 }
1993 }
1994 $atts['email_to'] = implode( ', ', $atts['email_to'] );
1995 }
1996
1997 private static function setup_new_notification( &$new_notification, $notification, $atts ) {
1998 // Set up new notification
1999 $new_notification = array(
2000 'post_content' => array(
2001 'email_to' => $atts['email_to'],
2002 'event' => $atts['event'],
2003 ),
2004 'post_name' => $atts['form_id'] . '_email_' . $atts['email_key'],
2005 );
2006
2007 // Add more fields to the new notification
2008 $add_fields = array( 'email_message', 'email_subject', 'plain_text', 'inc_user_info', 'conditions' );
2009 foreach ( $add_fields as $add_field ) {
2010 if ( isset( $notification[ $add_field ] ) ) {
2011 $new_notification['post_content'][ $add_field ] = $notification[ $add_field ];
2012 } elseif ( in_array( $add_field, array( 'plain_text', 'inc_user_info' ) ) ) {
2013 $new_notification['post_content'][ $add_field ] = 0;
2014 } else {
2015 $new_notification['post_content'][ $add_field ] = '';
2016 }
2017 unset( $add_field );
2018 }
2019
2020 // Set reply to
2021 $new_notification['post_content']['reply_to'] = $atts['reply_to'];
2022
2023 // Set from
2024 if ( ! empty( $atts['reply_to'] ) || ! empty( $atts['reply_to_name'] ) ) {
2025 $new_notification['post_content']['from'] = ( empty( $atts['reply_to_name'] ) ? '[sitename]' : $atts['reply_to_name'] ) . ' <' . ( empty( $atts['reply_to'] ) ? '[admin_email]' : $atts['reply_to'] ) . '>';
2026 }
2027 }
2028
2029 /**
2030 * Switch field IDs in pre-2.0 email conditional logic
2031 *
2032 * @param array $post_content Pass by reference.
2033 */
2034 private static function switch_email_condition_field_ids( &$post_content ) {
2035 // Switch field IDs in conditional logic
2036 if ( isset( $post_content['conditions'] ) && is_array( $post_content['conditions'] ) ) {
2037 foreach ( $post_content['conditions'] as $email_key => $val ) {
2038 if ( is_numeric( $email_key ) ) {
2039 $post_content['conditions'][ $email_key ] = self::switch_action_field_ids( $val, array( 'hide_field' ) );
2040 }
2041 unset( $email_key, $val );
2042 }
2043 }
2044 }
2045
2046 private static function migrate_autoresponder_to_action( $form_options, $form_id, &$notifications ) {
2047 if ( isset( $form_options['auto_responder'] ) && $form_options['auto_responder'] && isset( $form_options['ar_email_message'] ) && $form_options['ar_email_message'] ) {
2048 // migrate autoresponder
2049
2050 $email_field = isset( $form_options['ar_email_to'] ) ? $form_options['ar_email_to'] : 0;
2051 if ( strpos( $email_field, '|' ) ) {
2052 // data from entries field
2053 $email_field = explode( '|', $email_field );
2054 if ( isset( $email_field[1] ) ) {
2055 $email_field = $email_field[1];
2056 }
2057 }
2058 if ( is_numeric( $email_field ) && ! empty( $email_field ) ) {
2059 $email_field = '[' . $email_field . ']';
2060 }
2061
2062 $notification = $form_options;
2063 $new_notification2 = array(
2064 'post_content' => array(
2065 'email_message' => $notification['ar_email_message'],
2066 'email_subject' => isset( $notification['ar_email_subject'] ) ? $notification['ar_email_subject'] : '',
2067 'email_to' => $email_field,
2068 'plain_text' => isset( $notification['ar_plain_text'] ) ? $notification['ar_plain_text'] : 0,
2069 'inc_user_info' => 0,
2070 ),
2071 'post_name' => $form_id . '_email_' . count( $notifications ),
2072 );
2073
2074 $reply_to = isset( $notification['ar_reply_to'] ) ? $notification['ar_reply_to'] : '';
2075 $reply_to_name = isset( $notification['ar_reply_to_name'] ) ? $notification['ar_reply_to_name'] : '';
2076
2077 if ( ! empty( $reply_to ) ) {
2078 $new_notification2['post_content']['reply_to'] = $reply_to;
2079 }
2080
2081 if ( ! empty( $reply_to ) || ! empty( $reply_to_name ) ) {
2082 $new_notification2['post_content']['from'] = ( empty( $reply_to_name ) ? '[sitename]' : $reply_to_name ) . ' <' . ( empty( $reply_to ) ? '[admin_email]' : $reply_to ) . '>';
2083 }
2084
2085 $notifications[] = $new_notification2;
2086 unset( $new_notification2 );
2087 }//end if
2088 }
2089
2090 /**
2091 * PHP 8 backward compatibility for the libxml_disable_entity_loader function
2092 *
2093 * @param bool $loader
2094 *
2095 * @return bool
2096 */
2097 public static function maybe_libxml_disable_entity_loader( $loader ) {
2098 if ( version_compare( phpversion(), '8.0', '<' ) && function_exists( 'libxml_disable_entity_loader' ) ) {
2099 $loader = libxml_disable_entity_loader( $loader ); // phpcs:disable Generic.PHP.DeprecatedFunctions.Deprecated
2100 }
2101
2102 return $loader;
2103 }
2104
2105 public static function check_if_libxml_disable_entity_loader_exists() {
2106 return version_compare( phpversion(), '8.0', '<' ) && ! function_exists( 'libxml_disable_entity_loader' );
2107 }
2108
2109 /**
2110 * Get the file types allowed for importing.
2111 * This is used in the "accept" attribute for the file upload input on the XML import page.
2112 *
2113 * @since 6.8.3
2114 *
2115 * @return array
2116 */
2117 public static function get_supported_upload_file_types() {
2118 $file_types = array( '.xml' );
2119 if ( FrmAppHelper::pro_is_installed() ) {
2120 // CSV Importing is only available in Pro.
2121 $file_types[] = '.csv';
2122 }
2123 return $file_types;
2124 }
2125 }
2126