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

2,142 lines 64.1 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 if ( ! empty( $m['value']['map_address_fields'] ) ) {
1177 foreach ( $m['value']['map_address_fields'] as $address_field_key => $address_field_id ) {
1178 if ( isset( $frm_duplicate_ids[ $address_field_id ] ) ) {
1179 $m['value']['map_address_fields'][ $address_field_key ] = $frm_duplicate_ids[ $address_field_id ];
1180 }
1181 }
1182 }
1183
1184 if ( ! empty( $m['value']['calendar_options'] ) ) {
1185 foreach ( $m['value']['calendar_options'] as $calendar_option_group_key => $calendar_option ) {
1186 if ( isset( $frm_duplicate_ids[ $calendar_option['value'] ] ) ) {
1187 $m['value']['calendar_options'][ $calendar_option_group_key ]['value'] = $frm_duplicate_ids[ $calendar_option['value'] ];
1188 }
1189 }
1190 }
1191
1192 $check_dup_array = array();
1193 if ( ! empty( $m['value']['order_by'] ) ) {
1194 if ( is_numeric( $m['value']['order_by'] ) && isset( $frm_duplicate_ids[ $m['value']['order_by'] ] ) ) {
1195 $m['value']['order_by'] = $frm_duplicate_ids[ $m['value']['order_by'] ];
1196 } elseif ( is_array( $m['value']['order_by'] ) ) {
1197 $check_dup_array[] = 'order_by';
1198 }
1199 }
1200
1201 if ( ! empty( $m['value']['where'] ) ) {
1202 $check_dup_array[] = 'where';
1203 }
1204
1205 foreach ( $check_dup_array as $check_k ) {
1206 foreach ( (array) $m['value'][ $check_k ] as $mk => $mv ) {
1207 if ( isset( $frm_duplicate_ids[ $mv ] ) ) {
1208 $m['value'][ $check_k ][ $mk ] = $frm_duplicate_ids[ $mv ];
1209 }
1210 unset( $mk, $mv );
1211 }
1212 }
1213 }//end if
1214 }//end if
1215 }//end if
1216
1217 if ( ! is_array( $m['value'] ) ) {
1218 $m['value'] = FrmAppHelper::maybe_json_decode( $m['value'] );
1219 }
1220
1221 $post['postmeta'][ (string) $meta->meta_key ] = $m['value'];
1222 }
1223
1224 private static function populate_layout( &$post, $layout ) {
1225 $post['layout'][ (string) $layout->type ] = (string) $layout->data;
1226 }
1227
1228 /**
1229 * Add terms to post
1230 *
1231 * @param array $post By reference.
1232 * @param object $item The XML object data.
1233 */
1234 private static function populate_taxonomies( &$post, $item ) {
1235 foreach ( $item->category as $c ) {
1236 $att = $c->attributes();
1237 if ( ! isset( $att['nicename'] ) ) {
1238 continue;
1239 }
1240
1241 $taxonomy = (string) $att['domain'];
1242 if ( is_taxonomy_hierarchical( $taxonomy ) ) {
1243 $name = (string) $att['nicename'];
1244 $h_term = get_term_by( 'slug', $name, $taxonomy );
1245 if ( $h_term ) {
1246 $name = $h_term->term_id;
1247 }
1248 unset( $h_term );
1249 } else {
1250 $name = (string) $c;
1251 }
1252
1253 if ( ! isset( $post['tax_input'][ $taxonomy ] ) ) {
1254 $post['tax_input'][ $taxonomy ] = array();
1255 }
1256
1257 $post['tax_input'][ $taxonomy ][] = $name;
1258 unset( $name );
1259 }//end foreach
1260 }
1261
1262 /**
1263 * Edit post if the key and created time match
1264 */
1265 private static function maybe_editing_post( &$post ) {
1266 $match_by = array(
1267 'post_type' => $post['post_type'],
1268 'name' => $post['post_name'],
1269 'post_status' => $post['post_status'],
1270 'posts_per_page' => 1,
1271 );
1272
1273 if ( in_array( $post['post_status'], array( 'trash', 'draft' ) ) ) {
1274 $match_by['include'] = $post['post_id'];
1275 unset( $match_by['name'] );
1276 }
1277
1278 $editing = get_posts( $match_by );
1279
1280 if ( ! empty( $editing ) && current( $editing )->post_date == $post['post_date'] ) {
1281 // set the id of the post to edit
1282 $post['ID'] = current( $editing )->ID;
1283 }
1284 }
1285
1286 /**
1287 * @param array $post
1288 * @param int $post_id
1289 * @return void
1290 */
1291 private static function update_postmeta( &$post, $post_id ) {
1292 foreach ( $post['postmeta'] as $k => $v ) {
1293 switch ( $k ) {
1294 case '_edit_last':
1295 $v = FrmAppHelper::get_user_id_param( $v );
1296 break;
1297
1298 case '_thumbnail_id':
1299 if ( FrmAppHelper::pro_is_installed() ) {
1300 // Change the attachment ID.
1301 $field_obj = FrmFieldFactory::get_field_type( 'file' );
1302 $v = $field_obj->get_file_id( $v );
1303 }
1304 break;
1305
1306 case 'frm_dyncontent':
1307 if ( is_array( $v ) ) {
1308 $v = json_encode( $v );
1309 }
1310 break;
1311
1312 case 'frm_param':
1313 add_rewrite_endpoint( $v, EP_PERMALINK | EP_PAGES );
1314 break;
1315 }//end switch
1316
1317 update_post_meta( $post_id, $k, $v );
1318 unset( $k, $v );
1319 }//end foreach
1320 }
1321
1322 /**
1323 * @param array $post
1324 * @param int $post_id
1325 */
1326 private static function update_layout( &$post, $post_id ) {
1327 if ( is_callable( 'FrmViewsLayout::maybe_create_layouts_for_view' ) ) {
1328 $listing_layout = ! empty( $post['layout']['listing'] ) ? json_decode( $post['layout']['listing'], true ) : array();
1329 $detail_layout = ! empty( $post['layout']['detail'] ) ? json_decode( $post['layout']['detail'], true ) : array();
1330 if ( $listing_layout || $detail_layout ) {
1331 FrmViewsLayout::maybe_create_layouts_for_view( $post_id, $listing_layout, $detail_layout );
1332 }
1333 }
1334 }
1335
1336 private static function maybe_update_stylesheet( $imported ) {
1337 $new_styles = ! empty( $imported['imported']['styles'] );
1338 $updated_styles = ! empty( $imported['updated']['styles'] );
1339 if ( $new_styles || $updated_styles ) {
1340 if ( is_admin() && function_exists( 'get_filesystem_method' ) ) {
1341 $frm_style = new FrmStyle();
1342 $frm_style->update( 'default' );
1343 }
1344 foreach ( $imported['forms'] as $form_id ) {
1345 self::update_custom_style_setting_after_import( $form_id );
1346 }
1347 }
1348 }
1349
1350 /**
1351 * @param mixed $result
1352 * @param string $message
1353 * @param array $errors
1354 */
1355 public static function parse_message( $result, &$message, &$errors ) {
1356 if ( is_wp_error( $result ) ) {
1357 $errors[] = $result->get_error_message();
1358
1359 // Remove the SimpleXML_parse_error from the WP_Error object to avoid
1360 // displaying duplicate error messages from $result->get_error_message()
1361 $error_codes = $result->get_error_codes();
1362 $error_details = array();
1363 foreach ( $error_codes as $error_code ) {
1364 // Clone WP_Error data because WP_Error removes all error messages and data
1365 // associated with the specified error code when an item is removed.
1366 // Source: https://developer.wordpress.org/reference/classes/wp_error/remove/#source
1367 $error_details = $result->get_error_data( $error_code );
1368 if ( $error_code === 'SimpleXML_parse_error' ) {
1369 $result->remove( $error_code );
1370 break;
1371 }
1372 }
1373
1374 if ( ! empty( $error_details ) ) {
1375 $errors[] = '<br />' . esc_html_x( 'Error details:', 'import xml message', 'formidable' ) . '<br />' . esc_html( print_r( $error_details, 1 ) );
1376 }
1377
1378 return;
1379 }//end if
1380
1381 if ( ! $result ) {
1382 return;
1383 }
1384
1385 if ( ! is_array( $result ) ) {
1386 $message = is_string( $result ) ? $result : htmlentities( print_r( $result, 1 ) );
1387 return;
1388 }
1389
1390 $t_strings = array(
1391 'imported' => __( 'Imported', 'formidable' ),
1392 'updated' => __( 'Updated', 'formidable' ),
1393 );
1394
1395 $message = '<ul>';
1396 foreach ( $result as $type => $results ) {
1397 if ( ! isset( $t_strings[ $type ] ) ) {
1398 // only print imported and updated
1399 continue;
1400 }
1401
1402 $s_message = array();
1403 foreach ( $results as $k => $m ) {
1404 self::item_count_message( $m, $k, $s_message );
1405 unset( $k, $m );
1406 }
1407
1408 if ( ! empty( $s_message ) ) {
1409 $message .= '<li><strong>' . $t_strings[ $type ] . ':</strong> ';
1410 $message .= implode( ', ', $s_message );
1411 $message .= '</li>';
1412 }
1413 }
1414
1415 if ( $message === '<ul>' ) {
1416 $message = '';
1417 $errors[] = __( 'Nothing was imported or updated', 'formidable' );
1418 } else {
1419 self::add_form_link_to_message( $result, $message );
1420
1421 /**
1422 * @since 5.3
1423 *
1424 * @param string $message
1425 * @param array $result
1426 */
1427 $message = apply_filters( 'frm_xml_parsed_message', $message, $result );
1428 $message .= '</ul>';
1429 }
1430 }
1431
1432 /**
1433 * @param int $m
1434 * @param string $type
1435 * @param array<string> $s_message
1436 */
1437 public static function item_count_message( $m, $type, &$s_message ) {
1438 if ( ! $m ) {
1439 return;
1440 }
1441
1442 $strings = array(
1443 /* translators: %1$s: Number of items */
1444 'forms' => sprintf( _n( '%1$s Form', '%1$s Forms', $m, 'formidable' ), $m ),
1445 /* translators: %1$s: Number of items */
1446 'fields' => sprintf( _n( '%1$s Field', '%1$s Fields', $m, 'formidable' ), $m ),
1447 /* translators: %1$s: Number of items */
1448 'items' => sprintf( _n( '%1$s Entry', '%1$s Entries', $m, 'formidable' ), $m ),
1449 /* translators: %1$s: Number of items */
1450 'views' => sprintf( _n( '%1$s View', '%1$s Views', $m, 'formidable' ), $m ),
1451 /* translators: %1$s: Number of items */
1452 'posts' => sprintf( _n( '%1$s Page/Post', '%1$s Pages/Posts', $m, 'formidable' ), $m ),
1453 /* translators: %1$s: Number of items */
1454 'styles' => sprintf( _n( '%1$s Style', '%1$s Styles', $m, 'formidable' ), $m ),
1455 /* translators: %1$s: Number of items */
1456 'terms' => sprintf( _n( '%1$s Term', '%1$s Terms', $m, 'formidable' ), $m ),
1457 /* translators: %1$s: Number of items */
1458 'actions' => sprintf( _n( '%1$s Form Action', '%1$s Form Actions', $m, 'formidable' ), $m ),
1459 );
1460
1461 if ( isset( $strings[ $type ] ) ) {
1462 $s_message[] = $strings[ $type ];
1463 } else {
1464 $string = ' ' . $m . ' ' . ucfirst( $type );
1465
1466 /**
1467 * @since 5.3
1468 *
1469 * @param string $string Message string for imported item.
1470 * @param int $m Number of item that was imported.
1471 * }
1472 */
1473 $string = apply_filters( 'frm_xml_' . $type . '_count_message', $string, $m );
1474 $s_message[] = $string;
1475 }
1476 }
1477
1478 /**
1479 * If a single form was imported, include a link in the success message.
1480 *
1481 * @since 4.0
1482 * @param array $result The response from the XML import.
1483 * @param string $message The response shown on the page after import.
1484 */
1485 private static function add_form_link_to_message( $result, &$message ) {
1486 $total_forms = $result['imported']['forms'] + $result['updated']['forms'];
1487 if ( $total_forms > 1 ) {
1488 return;
1489 }
1490
1491 $primary_form = reset( $result['forms'] );
1492 if ( ! empty( $primary_form ) ) {
1493 $primary_form = FrmForm::getOne( $primary_form );
1494 $form_id = empty( $primary_form->parent_form_id ) ? $primary_form->id : $primary_form->parent_form_id;
1495
1496 $message .= '<li><a href="' . esc_url( FrmForm::get_edit_link( $form_id ) ) . '">' . esc_html__( 'Go to imported form', 'formidable' ) . '</a></li>';
1497 }
1498 }
1499
1500 /**
1501 * Prepare the form options for export
1502 *
1503 * @since 2.0.19
1504 *
1505 * @param string $options
1506 *
1507 * @return string
1508 */
1509 public static function prepare_form_options_for_export( $options ) {
1510 FrmAppHelper::unserialize_or_decode( $options );
1511 // Change custom_style to the post_name instead of ID (1 may be a string)
1512 $not_default = isset( $options['custom_style'] ) && 1 != $options['custom_style'];
1513 if ( $not_default ) {
1514 global $wpdb;
1515 $table = $wpdb->prefix . 'posts';
1516 $where = array( 'ID' => $options['custom_style'] );
1517 $select = 'post_name';
1518
1519 $style_name = FrmDb::get_var( $table, $where, $select );
1520
1521 if ( $style_name ) {
1522 $options['custom_style'] = $style_name;
1523 } else {
1524 $options['custom_style'] = 1;
1525 }
1526 }
1527 self::remove_default_form_options( $options );
1528 $options = serialize( $options );
1529
1530 return self::cdata( $options );
1531 }
1532
1533 /**
1534 * If the saved value is the same as the default, remove it from the export
1535 * This keeps file size down and prevents overriding global settings after import
1536 *
1537 * @since 3.06
1538 */
1539 private static function remove_default_form_options( &$options ) {
1540 $defaults = FrmFormsHelper::get_default_opts();
1541 if ( is_callable( 'FrmProFormsHelper::get_default_opts' ) ) {
1542 $defaults += FrmProFormsHelper::get_default_opts();
1543 }
1544 self::remove_defaults( $defaults, $options );
1545 }
1546
1547 /**
1548 * Remove extra settings from field to keep file size down
1549 *
1550 * @since 3.06
1551 */
1552 public static function prepare_field_for_export( &$field ) {
1553 self::remove_default_field_options( $field );
1554 self::add_image_src_to_image_options( $field );
1555 }
1556
1557 /**
1558 * Remove defaults from field options too
1559 *
1560 * @since 3.06
1561 */
1562 private static function remove_default_field_options( &$field ) {
1563 $defaults = self::default_field_options( $field->type );
1564 if ( empty( $defaults['blank'] ) ) {
1565 $global_settings = new FrmSettings();
1566 $global_defaults = $global_settings->default_options();
1567 $defaults['blank'] = $global_defaults['blank_msg'];
1568 }
1569
1570 $options = $field->field_options;
1571 FrmAppHelper::unserialize_or_decode( $options );
1572 self::remove_defaults( $defaults, $options );
1573 self::remove_default_html( 'custom_html', $defaults, $options );
1574
1575 // Get variations on the defaults.
1576 if ( isset( $options['invalid'] ) ) {
1577 $defaults = array(
1578 /* translators: %s: Field name */
1579 'invalid' => sprintf( __( '%s is invalid', 'formidable' ), $field->name ),
1580 );
1581 self::remove_defaults( $defaults, $options );
1582 }
1583
1584 $field->field_options = serialize( $options );
1585 }
1586
1587 /**
1588 * Add image "src" key to each image option so the image can be imported to another website.
1589 *
1590 * @since 5.5.1
1591 *
1592 * @param stdClass $field
1593 * @return void
1594 */
1595 private static function add_image_src_to_image_options( $field ) {
1596 if ( empty( $field->options ) || false === strpos( $field->options, 'image' ) ) {
1597 return;
1598 }
1599
1600 $updated = false;
1601 $options = $field->options;
1602 FrmAppHelper::unserialize_or_decode( $options );
1603
1604 if ( ! $options || ! is_array( $options ) ) {
1605 return;
1606 }
1607
1608 foreach ( $options as $key => $option ) {
1609 if ( is_array( $option ) && ! empty( $option['image'] ) ) {
1610 $options[ $key ]['src'] = wp_get_attachment_url( $option['image'] );
1611 $updated = true;
1612 }
1613 }
1614
1615 if ( $updated ) {
1616 $field->options = maybe_serialize( $options );
1617 }
1618 }
1619
1620 /**
1621 * @since 3.06.03
1622 */
1623 private static function default_field_options( $type ) {
1624 $defaults = FrmFieldsHelper::get_default_field_options( $type );
1625 if ( empty( $defaults['custom_html'] ) ) {
1626 $defaults['custom_html'] = FrmFieldsHelper::get_default_html( $type );
1627 }
1628 return $defaults;
1629 }
1630
1631 /**
1632 * Compare the default array to the saved values and
1633 * remove if they are the same
1634 *
1635 * @since 3.06
1636 */
1637 private static function remove_defaults( $defaults, &$saved ) {
1638 foreach ( $saved as $key => $value ) {
1639 if ( isset( $defaults[ $key ] ) && $defaults[ $key ] === $value ) {
1640 unset( $saved[ $key ] );
1641 }
1642 }
1643 }
1644
1645 /**
1646 * The line endings may prevent html from being equal when it should
1647 *
1648 * @since 3.06
1649 */
1650 private static function remove_default_html( $html_name, $defaults, &$options ) {
1651 if ( ! isset( $options[ $html_name ] ) || ! isset( $defaults[ $html_name ] ) ) {
1652 return;
1653 }
1654
1655 $old_html = str_replace( "\r\n", "\n", $options[ $html_name ] );
1656 $default_html = $defaults[ $html_name ];
1657 if ( $old_html == $default_html ) {
1658 unset( $options[ $html_name ] );
1659
1660 return;
1661 }
1662
1663 // Account for some of the older field default HTML.
1664 $default_html = str_replace( ' id="frm_desc_field_[key]"', '', $default_html );
1665 if ( $old_html == $default_html ) {
1666 unset( $options[ $html_name ] );
1667 }
1668 }
1669
1670 public static function cdata( $str ) {
1671 FrmAppHelper::unserialize_or_decode( $str );
1672 if ( is_array( $str ) ) {
1673 $str = json_encode( $str );
1674 } elseif ( seems_utf8( $str ) === false ) {
1675 $str = FrmAppHelper::maybe_utf8_encode( $str );
1676 }
1677
1678 if ( is_numeric( $str ) ) {
1679 return $str;
1680 }
1681
1682 self::remove_invalid_characters_from_xml( $str );
1683
1684 // $str = ent2ncr(esc_html( $str));
1685 $str = '<![CDATA[' . str_replace( ']]>', ']]]]><![CDATA[>', $str ) . ']]>';
1686
1687 return $str;
1688 }
1689
1690 /**
1691 * Remove <US> character (unit separator) from exported strings
1692 *
1693 * @since 2.0.22
1694 *
1695 * @param string $str
1696 */
1697 private static function remove_invalid_characters_from_xml( &$str ) {
1698 // Remove <US> character
1699 $str = str_replace( '\x1F', '', $str );
1700 }
1701
1702 public static function migrate_form_settings_to_actions( $form_options, $form_id, &$imported = array(), $switch = false ) {
1703 // Get post type
1704 $post_type = FrmFormActionsController::$action_post_type;
1705
1706 // Set up imported index, if not set up yet
1707 if ( ! isset( $imported['imported']['actions'] ) ) {
1708 $imported['imported']['actions'] = 0;
1709 }
1710
1711 // Migrate post settings to action
1712 self::migrate_post_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1713
1714 // Migrate email settings to action
1715 self::migrate_email_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1716 }
1717
1718 /**
1719 * Migrate post settings to form action
1720 *
1721 * @param array $form_options
1722 * @param int $form_id
1723 * @param string $post_type
1724 * @param array $imported
1725 * @param bool $switch
1726 */
1727 private static function migrate_post_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1728 if ( ! isset( $form_options['create_post'] ) || ! $form_options['create_post'] ) {
1729 return;
1730 }
1731
1732 $new_action = array(
1733 'post_type' => $post_type,
1734 'post_excerpt' => 'wppost',
1735 'post_title' => __( 'Create Posts', 'formidable' ),
1736 'menu_order' => $form_id,
1737 'post_status' => 'publish',
1738 'post_content' => array(),
1739 'post_name' => $form_id . '_wppost_1',
1740 );
1741
1742 $post_settings = array(
1743 'post_type',
1744 'post_category',
1745 'post_content',
1746 'post_excerpt',
1747 'post_title',
1748 'post_name',
1749 'post_date',
1750 'post_status',
1751 'post_custom_fields',
1752 'post_password',
1753 'post_parent',
1754 );
1755
1756 foreach ( $post_settings as $post_setting ) {
1757 if ( isset( $form_options[ $post_setting ] ) ) {
1758 $new_action['post_content'][ $post_setting ] = $form_options[ $post_setting ];
1759 }
1760 unset( $post_setting );
1761 }
1762
1763 $new_action['event'] = array( 'create', 'update' );
1764
1765 if ( $switch ) {
1766 // Fields with string or int saved.
1767 $basic_fields = array(
1768 'post_title',
1769 'post_content',
1770 'post_excerpt',
1771 'post_password',
1772 'post_date',
1773 'post_status',
1774 'post_parent',
1775 );
1776
1777 // Fields with arrays saved.
1778 $array_fields = array( 'post_category', 'post_custom_fields' );
1779
1780 $new_action['post_content'] = self::switch_action_field_ids( $new_action['post_content'], $basic_fields, $array_fields );
1781 }
1782 $new_action['post_content'] = json_encode( $new_action['post_content'] );
1783
1784 $exists = get_posts(
1785 array(
1786 'name' => $new_action['post_name'],
1787 'post_type' => $new_action['post_type'],
1788 'post_status' => $new_action['post_status'],
1789 'numberposts' => 1,
1790 )
1791 );
1792
1793 if ( ! $exists ) {
1794 // this isn't an email, but we need to use a class that will always be included
1795 FrmDb::save_json_post( $new_action );
1796 ++$imported['imported']['actions'];
1797 }
1798 }
1799
1800 /**
1801 * Switch old field IDs for new field IDs in emails and post
1802 *
1803 * @since 2.0
1804 *
1805 * @param array $post_content Check for old field IDs.
1806 * @param array $basic_fields Fields with string or int saved.
1807 * @param array $array_fields Fields with arrays saved.
1808 *
1809 * @return string $post_content - new field IDs
1810 */
1811 private static function switch_action_field_ids( $post_content, $basic_fields, $array_fields = array() ) {
1812 global $frm_duplicate_ids;
1813
1814 // If there aren't IDs that were switched, end now
1815 if ( ! $frm_duplicate_ids ) {
1816 return;
1817 }
1818
1819 // Get old IDs
1820 $old = array_keys( $frm_duplicate_ids );
1821
1822 // Get new IDs
1823 $new = array_values( $frm_duplicate_ids );
1824
1825 // Do a str_replace with each item to set the new IDs
1826 foreach ( $post_content as $key => $setting ) {
1827 if ( ! is_array( $setting ) && in_array( $key, $basic_fields ) ) {
1828 // Replace old IDs with new IDs
1829 $post_content[ $key ] = str_replace( $old, $new, $setting );
1830 } elseif ( is_array( $setting ) && in_array( $key, $array_fields ) ) {
1831 foreach ( $setting as $k => $val ) {
1832 // Replace old IDs with new IDs
1833 $post_content[ $key ][ $k ] = str_replace( $old, $new, $val );
1834 }
1835 }
1836 unset( $key, $setting );
1837 }
1838
1839 return $post_content;
1840 }
1841
1842 private static function migrate_email_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1843 // No old notifications or autoresponders to carry over
1844 if ( ! isset( $form_options['auto_responder'] ) && ! isset( $form_options['notification'] ) && ! isset( $form_options['email_to'] ) ) {
1845 return;
1846 }
1847
1848 // Initialize notifications array
1849 $notifications = array();
1850
1851 // Migrate regular notifications
1852 self::migrate_notifications_to_action( $form_options, $form_id, $notifications );
1853
1854 // Migrate autoresponders
1855 self::migrate_autoresponder_to_action( $form_options, $form_id, $notifications );
1856
1857 if ( empty( $notifications ) ) {
1858 return;
1859 }
1860
1861 foreach ( $notifications as $new_notification ) {
1862 $new_notification['post_type'] = $post_type;
1863 $new_notification['post_excerpt'] = 'email';
1864 $new_notification['post_title'] = __( 'Email Notification', 'formidable' );
1865 $new_notification['menu_order'] = $form_id;
1866 $new_notification['post_status'] = 'publish';
1867
1868 // Switch field IDs and keys, if needed
1869 if ( $switch ) {
1870
1871 // Switch field IDs in email conditional logic
1872 self::switch_email_condition_field_ids( $new_notification['post_content'] );
1873
1874 // Switch all other field IDs in email
1875 $new_notification['post_content'] = FrmFieldsHelper::switch_field_ids( $new_notification['post_content'] );
1876 }
1877 $new_notification['post_content'] = FrmAppHelper::prepare_and_encode( $new_notification['post_content'] );
1878
1879 $exists = get_posts(
1880 array(
1881 'name' => $new_notification['post_name'],
1882 'post_type' => $new_notification['post_type'],
1883 'post_status' => $new_notification['post_status'],
1884 'numberposts' => 1,
1885 )
1886 );
1887
1888 if ( empty( $exists ) ) {
1889 FrmDb::save_json_post( $new_notification );
1890 ++$imported['imported']['actions'];
1891 }
1892 unset( $new_notification );
1893 }//end foreach
1894
1895 self::remove_deprecated_notification_settings( $form_id, $form_options );
1896 }
1897
1898 /**
1899 * Remove deprecated notification settings after migration
1900 *
1901 * @since 2.05
1902 *
1903 * @param int|string $form_id
1904 * @param array $form_options
1905 */
1906 private static function remove_deprecated_notification_settings( $form_id, $form_options ) {
1907 $delete_settings = array( 'notification', 'autoresponder', 'email_to' );
1908 foreach ( $delete_settings as $index ) {
1909 if ( isset( $form_options[ $index ] ) ) {
1910 unset( $form_options[ $index ] );
1911 }
1912 }
1913 FrmForm::update( $form_id, array( 'options' => $form_options ) );
1914 }
1915
1916 private static function migrate_notifications_to_action( $form_options, $form_id, &$notifications ) {
1917 if ( ! isset( $form_options['notification'] ) && ! empty( $form_options['email_to'] ) ) {
1918 // add old settings into notification array
1919 $form_options['notification'] = array( 0 => $form_options );
1920 } elseif ( isset( $form_options['notification']['email_to'] ) ) {
1921 // make sure it's in the correct format
1922 $form_options['notification'] = array( 0 => $form_options['notification'] );
1923 }
1924
1925 if ( isset( $form_options['notification'] ) && is_array( $form_options['notification'] ) ) {
1926 foreach ( $form_options['notification'] as $email_key => $notification ) {
1927
1928 $atts = array(
1929 'email_to' => '',
1930 'reply_to' => '',
1931 'reply_to_name' => '',
1932 'event' => '',
1933 'form_id' => $form_id,
1934 'email_key' => $email_key,
1935 );
1936
1937 // Format the email data
1938 self::format_email_data( $atts, $notification );
1939
1940 if ( isset( $notification['twilio'] ) && $notification['twilio'] ) {
1941 do_action( 'frm_create_twilio_action', $atts, $notification );
1942 }
1943
1944 // Setup the new notification
1945 $new_notification = array();
1946 self::setup_new_notification( $new_notification, $notification, $atts );
1947
1948 $notifications[] = $new_notification;
1949 }//end foreach
1950 }//end if
1951 }
1952
1953 private static function format_email_data( &$atts, $notification ) {
1954 // Format email_to
1955 self::format_email_to_data( $atts, $notification );
1956
1957 // Format the reply to email and name
1958 $reply_fields = array(
1959 'reply_to' => '',
1960 'reply_to_name' => '',
1961 );
1962 foreach ( $reply_fields as $f => $val ) {
1963 if ( isset( $notification[ $f ] ) ) {
1964 $atts[ $f ] = $notification[ $f ];
1965 if ( 'custom' == $notification[ $f ] ) {
1966 $atts[ $f ] = $notification[ 'cust_' . $f ];
1967 } elseif ( is_numeric( $atts[ $f ] ) && ! empty( $atts[ $f ] ) ) {
1968 $atts[ $f ] = '[' . $atts[ $f ] . ']';
1969 }
1970 }
1971 unset( $f, $val );
1972 }
1973
1974 // Format event
1975 $atts['event'] = array( 'create' );
1976 if ( isset( $notification['update_email'] ) && 1 == $notification['update_email'] ) {
1977 $atts['event'][] = 'update';
1978 } elseif ( isset( $notification['update_email'] ) && 2 == $notification['update_email'] ) {
1979 $atts['event'] = array( 'update' );
1980 }
1981 }
1982
1983 private static function format_email_to_data( &$atts, $notification ) {
1984 if ( isset( $notification['email_to'] ) ) {
1985 $atts['email_to'] = preg_split( '/ (,|;) /', $notification['email_to'] );
1986 } else {
1987 $atts['email_to'] = array();
1988 }
1989
1990 if ( isset( $notification['also_email_to'] ) ) {
1991 $email_fields = (array) $notification['also_email_to'];
1992 $atts['email_to'] = array_merge( $email_fields, $atts['email_to'] );
1993 unset( $email_fields );
1994 }
1995
1996 foreach ( $atts['email_to'] as $key => $email_field ) {
1997
1998 if ( is_numeric( $email_field ) ) {
1999 $atts['email_to'][ $key ] = '[' . $email_field . ']';
2000 }
2001
2002 if ( strpos( $email_field, '|' ) ) {
2003 $email_opt = explode( '|', $email_field );
2004 if ( isset( $email_opt[0] ) ) {
2005 $atts['email_to'][ $key ] = '[' . $email_opt[0] . ' show=' . $email_opt[1] . ']';
2006 }
2007 unset( $email_opt );
2008 }
2009 }
2010 $atts['email_to'] = implode( ', ', $atts['email_to'] );
2011 }
2012
2013 private static function setup_new_notification( &$new_notification, $notification, $atts ) {
2014 // Set up new notification
2015 $new_notification = array(
2016 'post_content' => array(
2017 'email_to' => $atts['email_to'],
2018 'event' => $atts['event'],
2019 ),
2020 'post_name' => $atts['form_id'] . '_email_' . $atts['email_key'],
2021 );
2022
2023 // Add more fields to the new notification
2024 $add_fields = array( 'email_message', 'email_subject', 'plain_text', 'inc_user_info', 'conditions' );
2025 foreach ( $add_fields as $add_field ) {
2026 if ( isset( $notification[ $add_field ] ) ) {
2027 $new_notification['post_content'][ $add_field ] = $notification[ $add_field ];
2028 } elseif ( in_array( $add_field, array( 'plain_text', 'inc_user_info' ) ) ) {
2029 $new_notification['post_content'][ $add_field ] = 0;
2030 } else {
2031 $new_notification['post_content'][ $add_field ] = '';
2032 }
2033 unset( $add_field );
2034 }
2035
2036 // Set reply to
2037 $new_notification['post_content']['reply_to'] = $atts['reply_to'];
2038
2039 // Set from
2040 if ( ! empty( $atts['reply_to'] ) || ! empty( $atts['reply_to_name'] ) ) {
2041 $new_notification['post_content']['from'] = ( empty( $atts['reply_to_name'] ) ? '[sitename]' : $atts['reply_to_name'] ) . ' <' . ( empty( $atts['reply_to'] ) ? '[admin_email]' : $atts['reply_to'] ) . '>';
2042 }
2043 }
2044
2045 /**
2046 * Switch field IDs in pre-2.0 email conditional logic
2047 *
2048 * @param array $post_content Pass by reference.
2049 */
2050 private static function switch_email_condition_field_ids( &$post_content ) {
2051 // Switch field IDs in conditional logic
2052 if ( isset( $post_content['conditions'] ) && is_array( $post_content['conditions'] ) ) {
2053 foreach ( $post_content['conditions'] as $email_key => $val ) {
2054 if ( is_numeric( $email_key ) ) {
2055 $post_content['conditions'][ $email_key ] = self::switch_action_field_ids( $val, array( 'hide_field' ) );
2056 }
2057 unset( $email_key, $val );
2058 }
2059 }
2060 }
2061
2062 private static function migrate_autoresponder_to_action( $form_options, $form_id, &$notifications ) {
2063 if ( isset( $form_options['auto_responder'] ) && $form_options['auto_responder'] && isset( $form_options['ar_email_message'] ) && $form_options['ar_email_message'] ) {
2064 // migrate autoresponder
2065
2066 $email_field = isset( $form_options['ar_email_to'] ) ? $form_options['ar_email_to'] : 0;
2067 if ( strpos( $email_field, '|' ) ) {
2068 // data from entries field
2069 $email_field = explode( '|', $email_field );
2070 if ( isset( $email_field[1] ) ) {
2071 $email_field = $email_field[1];
2072 }
2073 }
2074 if ( is_numeric( $email_field ) && ! empty( $email_field ) ) {
2075 $email_field = '[' . $email_field . ']';
2076 }
2077
2078 $notification = $form_options;
2079 $new_notification2 = array(
2080 'post_content' => array(
2081 'email_message' => $notification['ar_email_message'],
2082 'email_subject' => isset( $notification['ar_email_subject'] ) ? $notification['ar_email_subject'] : '',
2083 'email_to' => $email_field,
2084 'plain_text' => isset( $notification['ar_plain_text'] ) ? $notification['ar_plain_text'] : 0,
2085 'inc_user_info' => 0,
2086 ),
2087 'post_name' => $form_id . '_email_' . count( $notifications ),
2088 );
2089
2090 $reply_to = isset( $notification['ar_reply_to'] ) ? $notification['ar_reply_to'] : '';
2091 $reply_to_name = isset( $notification['ar_reply_to_name'] ) ? $notification['ar_reply_to_name'] : '';
2092
2093 if ( ! empty( $reply_to ) ) {
2094 $new_notification2['post_content']['reply_to'] = $reply_to;
2095 }
2096
2097 if ( ! empty( $reply_to ) || ! empty( $reply_to_name ) ) {
2098 $new_notification2['post_content']['from'] = ( empty( $reply_to_name ) ? '[sitename]' : $reply_to_name ) . ' <' . ( empty( $reply_to ) ? '[admin_email]' : $reply_to ) . '>';
2099 }
2100
2101 $notifications[] = $new_notification2;
2102 unset( $new_notification2 );
2103 }//end if
2104 }
2105
2106 /**
2107 * PHP 8 backward compatibility for the libxml_disable_entity_loader function
2108 *
2109 * @param bool $loader
2110 *
2111 * @return bool
2112 */
2113 public static function maybe_libxml_disable_entity_loader( $loader ) {
2114 if ( version_compare( phpversion(), '8.0', '<' ) && function_exists( 'libxml_disable_entity_loader' ) ) {
2115 $loader = libxml_disable_entity_loader( $loader ); // phpcs:disable Generic.PHP.DeprecatedFunctions.Deprecated
2116 }
2117
2118 return $loader;
2119 }
2120
2121 public static function check_if_libxml_disable_entity_loader_exists() {
2122 return version_compare( phpversion(), '8.0', '<' ) && ! function_exists( 'libxml_disable_entity_loader' );
2123 }
2124
2125 /**
2126 * Get the file types allowed for importing.
2127 * This is used in the "accept" attribute for the file upload input on the XML import page.
2128 *
2129 * @since 6.8.3
2130 *
2131 * @return array
2132 */
2133 public static function get_supported_upload_file_types() {
2134 $file_types = array( '.xml' );
2135 if ( FrmAppHelper::pro_is_installed() ) {
2136 // CSV Importing is only available in Pro.
2137 $file_types[] = '.csv';
2138 }
2139 return $file_types;
2140 }
2141 }
2142