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

2,125 lines 63.7 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 $installing_template 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 ( ! isset( $imported['form_status'] ) || 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'][ (int) $old_id ] = $post_id;
957
958 if ( $post['post_type'] === 'frm_display' ) {
959 $view_ids[ (int) $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 ( isset( $m['value']['order_by'] ) && ! 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 ( isset( $m['value']['where'] ) && ! 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 = isset( $imported['imported']['styles'] ) && ! empty( $imported['imported']['styles'] );
1322 $updated_styles = isset( $imported['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 } elseif ( ! $result ) {
1364 return;
1365 }//end if
1366
1367 if ( ! is_array( $result ) ) {
1368 $message = is_string( $result ) ? $result : htmlentities( print_r( $result, 1 ) );
1369
1370 return;
1371 }
1372
1373 $t_strings = array(
1374 'imported' => __( 'Imported', 'formidable' ),
1375 'updated' => __( 'Updated', 'formidable' ),
1376 );
1377
1378 $message = '<ul>';
1379 foreach ( $result as $type => $results ) {
1380 if ( ! isset( $t_strings[ $type ] ) ) {
1381 // only print imported and updated
1382 continue;
1383 }
1384
1385 $s_message = array();
1386 foreach ( $results as $k => $m ) {
1387 self::item_count_message( $m, $k, $s_message );
1388 unset( $k, $m );
1389 }
1390
1391 if ( ! empty( $s_message ) ) {
1392 $message .= '<li><strong>' . $t_strings[ $type ] . ':</strong> ';
1393 $message .= implode( ', ', $s_message );
1394 $message .= '</li>';
1395 }
1396 }
1397
1398 if ( $message == '<ul>' ) {
1399 $message = '';
1400 $errors[] = __( 'Nothing was imported or updated', 'formidable' );
1401 } else {
1402 self::add_form_link_to_message( $result, $message );
1403
1404 /**
1405 * @since 5.3
1406 *
1407 * @param string $message
1408 * @param array $result
1409 */
1410 $message = apply_filters( 'frm_xml_parsed_message', $message, $result );
1411 $message .= '</ul>';
1412 }
1413 }
1414
1415 /**
1416 * @param int $m
1417 * @param string $type
1418 * @param array<string> $s_message
1419 */
1420 public static function item_count_message( $m, $type, &$s_message ) {
1421 if ( ! $m ) {
1422 return;
1423 }
1424
1425 $strings = array(
1426 /* translators: %1$s: Number of items */
1427 'forms' => sprintf( _n( '%1$s Form', '%1$s Forms', $m, 'formidable' ), $m ),
1428 /* translators: %1$s: Number of items */
1429 'fields' => sprintf( _n( '%1$s Field', '%1$s Fields', $m, 'formidable' ), $m ),
1430 /* translators: %1$s: Number of items */
1431 'items' => sprintf( _n( '%1$s Entry', '%1$s Entries', $m, 'formidable' ), $m ),
1432 /* translators: %1$s: Number of items */
1433 'views' => sprintf( _n( '%1$s View', '%1$s Views', $m, 'formidable' ), $m ),
1434 /* translators: %1$s: Number of items */
1435 'posts' => sprintf( _n( '%1$s Page/Post', '%1$s Pages/Posts', $m, 'formidable' ), $m ),
1436 /* translators: %1$s: Number of items */
1437 'styles' => sprintf( _n( '%1$s Style', '%1$s Styles', $m, 'formidable' ), $m ),
1438 /* translators: %1$s: Number of items */
1439 'terms' => sprintf( _n( '%1$s Term', '%1$s Terms', $m, 'formidable' ), $m ),
1440 /* translators: %1$s: Number of items */
1441 'actions' => sprintf( _n( '%1$s Form Action', '%1$s Form Actions', $m, 'formidable' ), $m ),
1442 );
1443
1444 if ( isset( $strings[ $type ] ) ) {
1445 $s_message[] = $strings[ $type ];
1446 } else {
1447 $string = ' ' . $m . ' ' . ucfirst( $type );
1448
1449 /**
1450 * @since 5.3
1451 *
1452 * @param string $string Message string for imported item.
1453 * @param int $m Number of item that was imported.
1454 * }
1455 */
1456 $string = apply_filters( 'frm_xml_' . $type . '_count_message', $string, $m );
1457 $s_message[] = $string;
1458 }
1459 }
1460
1461 /**
1462 * If a single form was imported, include a link in the success message.
1463 *
1464 * @since 4.0
1465 * @param array $result The response from the XML import.
1466 * @param string $message The response shown on the page after import.
1467 */
1468 private static function add_form_link_to_message( $result, &$message ) {
1469 $total_forms = $result['imported']['forms'] + $result['updated']['forms'];
1470 if ( $total_forms > 1 ) {
1471 return;
1472 }
1473
1474 $primary_form = reset( $result['forms'] );
1475 if ( ! empty( $primary_form ) ) {
1476 $primary_form = FrmForm::getOne( $primary_form );
1477 $form_id = empty( $primary_form->parent_form_id ) ? $primary_form->id : $primary_form->parent_form_id;
1478
1479 $message .= '<li><a href="' . esc_url( FrmForm::get_edit_link( $form_id ) ) . '">' . esc_html__( 'Go to imported form', 'formidable' ) . '</a></li>';
1480 }
1481 }
1482
1483 /**
1484 * Prepare the form options for export
1485 *
1486 * @since 2.0.19
1487 *
1488 * @param string $options
1489 *
1490 * @return string
1491 */
1492 public static function prepare_form_options_for_export( $options ) {
1493 FrmAppHelper::unserialize_or_decode( $options );
1494 // Change custom_style to the post_name instead of ID (1 may be a string)
1495 $not_default = isset( $options['custom_style'] ) && 1 != $options['custom_style'];
1496 if ( $not_default ) {
1497 global $wpdb;
1498 $table = $wpdb->prefix . 'posts';
1499 $where = array( 'ID' => $options['custom_style'] );
1500 $select = 'post_name';
1501
1502 $style_name = FrmDb::get_var( $table, $where, $select );
1503
1504 if ( $style_name ) {
1505 $options['custom_style'] = $style_name;
1506 } else {
1507 $options['custom_style'] = 1;
1508 }
1509 }
1510 self::remove_default_form_options( $options );
1511 $options = serialize( $options );
1512
1513 return self::cdata( $options );
1514 }
1515
1516 /**
1517 * If the saved value is the same as the default, remove it from the export
1518 * This keeps file size down and prevents overriding global settings after import
1519 *
1520 * @since 3.06
1521 */
1522 private static function remove_default_form_options( &$options ) {
1523 $defaults = FrmFormsHelper::get_default_opts();
1524 if ( is_callable( 'FrmProFormsHelper::get_default_opts' ) ) {
1525 $defaults += FrmProFormsHelper::get_default_opts();
1526 }
1527 self::remove_defaults( $defaults, $options );
1528 }
1529
1530 /**
1531 * Remove extra settings from field to keep file size down
1532 *
1533 * @since 3.06
1534 */
1535 public static function prepare_field_for_export( &$field ) {
1536 self::remove_default_field_options( $field );
1537 self::add_image_src_to_image_options( $field );
1538 }
1539
1540 /**
1541 * Remove defaults from field options too
1542 *
1543 * @since 3.06
1544 */
1545 private static function remove_default_field_options( &$field ) {
1546 $defaults = self::default_field_options( $field->type );
1547 if ( empty( $defaults['blank'] ) ) {
1548 $global_settings = new FrmSettings();
1549 $global_defaults = $global_settings->default_options();
1550 $defaults['blank'] = $global_defaults['blank_msg'];
1551 }
1552
1553 $options = $field->field_options;
1554 FrmAppHelper::unserialize_or_decode( $options );
1555 self::remove_defaults( $defaults, $options );
1556 self::remove_default_html( 'custom_html', $defaults, $options );
1557
1558 // Get variations on the defaults.
1559 if ( isset( $options['invalid'] ) ) {
1560 $defaults = array(
1561 /* translators: %s: Field name */
1562 'invalid' => sprintf( __( '%s is invalid', 'formidable' ), $field->name ),
1563 );
1564 self::remove_defaults( $defaults, $options );
1565 }
1566
1567 $field->field_options = serialize( $options );
1568 }
1569
1570 /**
1571 * Add image "src" key to each image option so the image can be imported to another website.
1572 *
1573 * @since 5.5.1
1574 *
1575 * @param stdClass $field
1576 * @return void
1577 */
1578 private static function add_image_src_to_image_options( $field ) {
1579 if ( empty( $field->options ) || false === strpos( $field->options, 'image' ) ) {
1580 return;
1581 }
1582
1583 $updated = false;
1584 $options = $field->options;
1585 FrmAppHelper::unserialize_or_decode( $options );
1586
1587 if ( ! $options || ! is_array( $options ) ) {
1588 return;
1589 }
1590
1591 foreach ( $options as $key => $option ) {
1592 if ( is_array( $option ) && ! empty( $option['image'] ) ) {
1593 $options[ $key ]['src'] = wp_get_attachment_url( $option['image'] );
1594 $updated = true;
1595 }
1596 }
1597
1598 if ( $updated ) {
1599 $field->options = maybe_serialize( $options );
1600 }
1601 }
1602
1603 /**
1604 * @since 3.06.03
1605 */
1606 private static function default_field_options( $type ) {
1607 $defaults = FrmFieldsHelper::get_default_field_options( $type );
1608 if ( empty( $defaults['custom_html'] ) ) {
1609 $defaults['custom_html'] = FrmFieldsHelper::get_default_html( $type );
1610 }
1611 return $defaults;
1612 }
1613
1614 /**
1615 * Compare the default array to the saved values and
1616 * remove if they are the same
1617 *
1618 * @since 3.06
1619 */
1620 private static function remove_defaults( $defaults, &$saved ) {
1621 foreach ( $saved as $key => $value ) {
1622 if ( isset( $defaults[ $key ] ) && $defaults[ $key ] === $value ) {
1623 unset( $saved[ $key ] );
1624 }
1625 }
1626 }
1627
1628 /**
1629 * The line endings may prevent html from being equal when it should
1630 *
1631 * @since 3.06
1632 */
1633 private static function remove_default_html( $html_name, $defaults, &$options ) {
1634 if ( ! isset( $options[ $html_name ] ) || ! isset( $defaults[ $html_name ] ) ) {
1635 return;
1636 }
1637
1638 $old_html = str_replace( "\r\n", "\n", $options[ $html_name ] );
1639 $default_html = $defaults[ $html_name ];
1640 if ( $old_html == $default_html ) {
1641 unset( $options[ $html_name ] );
1642
1643 return;
1644 }
1645
1646 // Account for some of the older field default HTML.
1647 $default_html = str_replace( ' id="frm_desc_field_[key]"', '', $default_html );
1648 if ( $old_html == $default_html ) {
1649 unset( $options[ $html_name ] );
1650 }
1651 }
1652
1653 public static function cdata( $str ) {
1654 FrmAppHelper::unserialize_or_decode( $str );
1655 if ( is_array( $str ) ) {
1656 $str = json_encode( $str );
1657 } elseif ( seems_utf8( $str ) === false ) {
1658 $str = FrmAppHelper::maybe_utf8_encode( $str );
1659 }
1660
1661 if ( is_numeric( $str ) ) {
1662 return $str;
1663 }
1664
1665 self::remove_invalid_characters_from_xml( $str );
1666
1667 // $str = ent2ncr(esc_html( $str));
1668 $str = '<![CDATA[' . str_replace( ']]>', ']]]]><![CDATA[>', $str ) . ']]>';
1669
1670 return $str;
1671 }
1672
1673 /**
1674 * Remove <US> character (unit separator) from exported strings
1675 *
1676 * @since 2.0.22
1677 *
1678 * @param string $str
1679 */
1680 private static function remove_invalid_characters_from_xml( &$str ) {
1681 // Remove <US> character
1682 $str = str_replace( '\x1F', '', $str );
1683 }
1684
1685 public static function migrate_form_settings_to_actions( $form_options, $form_id, &$imported = array(), $switch = false ) {
1686 // Get post type
1687 $post_type = FrmFormActionsController::$action_post_type;
1688
1689 // Set up imported index, if not set up yet
1690 if ( ! isset( $imported['imported']['actions'] ) ) {
1691 $imported['imported']['actions'] = 0;
1692 }
1693
1694 // Migrate post settings to action
1695 self::migrate_post_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1696
1697 // Migrate email settings to action
1698 self::migrate_email_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1699 }
1700
1701 /**
1702 * Migrate post settings to form action
1703 *
1704 * @param array $form_options
1705 * @param int $form_id
1706 * @param string $post_type
1707 * @param array $imported
1708 * @param bool $switch
1709 */
1710 private static function migrate_post_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1711 if ( ! isset( $form_options['create_post'] ) || ! $form_options['create_post'] ) {
1712 return;
1713 }
1714
1715 $new_action = array(
1716 'post_type' => $post_type,
1717 'post_excerpt' => 'wppost',
1718 'post_title' => __( 'Create Posts', 'formidable' ),
1719 'menu_order' => $form_id,
1720 'post_status' => 'publish',
1721 'post_content' => array(),
1722 'post_name' => $form_id . '_wppost_1',
1723 );
1724
1725 $post_settings = array(
1726 'post_type',
1727 'post_category',
1728 'post_content',
1729 'post_excerpt',
1730 'post_title',
1731 'post_name',
1732 'post_date',
1733 'post_status',
1734 'post_custom_fields',
1735 'post_password',
1736 'post_parent',
1737 );
1738
1739 foreach ( $post_settings as $post_setting ) {
1740 if ( isset( $form_options[ $post_setting ] ) ) {
1741 $new_action['post_content'][ $post_setting ] = $form_options[ $post_setting ];
1742 }
1743 unset( $post_setting );
1744 }
1745
1746 $new_action['event'] = array( 'create', 'update' );
1747
1748 if ( $switch ) {
1749 // Fields with string or int saved.
1750 $basic_fields = array(
1751 'post_title',
1752 'post_content',
1753 'post_excerpt',
1754 'post_password',
1755 'post_date',
1756 'post_status',
1757 'post_parent',
1758 );
1759
1760 // Fields with arrays saved.
1761 $array_fields = array( 'post_category', 'post_custom_fields' );
1762
1763 $new_action['post_content'] = self::switch_action_field_ids( $new_action['post_content'], $basic_fields, $array_fields );
1764 }
1765 $new_action['post_content'] = json_encode( $new_action['post_content'] );
1766
1767 $exists = get_posts(
1768 array(
1769 'name' => $new_action['post_name'],
1770 'post_type' => $new_action['post_type'],
1771 'post_status' => $new_action['post_status'],
1772 'numberposts' => 1,
1773 )
1774 );
1775
1776 if ( ! $exists ) {
1777 // this isn't an email, but we need to use a class that will always be included
1778 FrmDb::save_json_post( $new_action );
1779 ++$imported['imported']['actions'];
1780 }
1781 }
1782
1783 /**
1784 * Switch old field IDs for new field IDs in emails and post
1785 *
1786 * @since 2.0
1787 *
1788 * @param array $post_content Check for old field IDs.
1789 * @param array $basic_fields Fields with string or int saved.
1790 * @param array $array_fields Fields with arrays saved.
1791 *
1792 * @return string $post_content - new field IDs
1793 */
1794 private static function switch_action_field_ids( $post_content, $basic_fields, $array_fields = array() ) {
1795 global $frm_duplicate_ids;
1796
1797 // If there aren't IDs that were switched, end now
1798 if ( ! $frm_duplicate_ids ) {
1799 return;
1800 }
1801
1802 // Get old IDs
1803 $old = array_keys( $frm_duplicate_ids );
1804
1805 // Get new IDs
1806 $new = array_values( $frm_duplicate_ids );
1807
1808 // Do a str_replace with each item to set the new IDs
1809 foreach ( $post_content as $key => $setting ) {
1810 if ( ! is_array( $setting ) && in_array( $key, $basic_fields ) ) {
1811 // Replace old IDs with new IDs
1812 $post_content[ $key ] = str_replace( $old, $new, $setting );
1813 } elseif ( is_array( $setting ) && in_array( $key, $array_fields ) ) {
1814 foreach ( $setting as $k => $val ) {
1815 // Replace old IDs with new IDs
1816 $post_content[ $key ][ $k ] = str_replace( $old, $new, $val );
1817 }
1818 }
1819 unset( $key, $setting );
1820 }
1821
1822 return $post_content;
1823 }
1824
1825 private static function migrate_email_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1826 // No old notifications or autoresponders to carry over
1827 if ( ! isset( $form_options['auto_responder'] ) && ! isset( $form_options['notification'] ) && ! isset( $form_options['email_to'] ) ) {
1828 return;
1829 }
1830
1831 // Initialize notifications array
1832 $notifications = array();
1833
1834 // Migrate regular notifications
1835 self::migrate_notifications_to_action( $form_options, $form_id, $notifications );
1836
1837 // Migrate autoresponders
1838 self::migrate_autoresponder_to_action( $form_options, $form_id, $notifications );
1839
1840 if ( empty( $notifications ) ) {
1841 return;
1842 }
1843
1844 foreach ( $notifications as $new_notification ) {
1845 $new_notification['post_type'] = $post_type;
1846 $new_notification['post_excerpt'] = 'email';
1847 $new_notification['post_title'] = __( 'Email Notification', 'formidable' );
1848 $new_notification['menu_order'] = $form_id;
1849 $new_notification['post_status'] = 'publish';
1850
1851 // Switch field IDs and keys, if needed
1852 if ( $switch ) {
1853
1854 // Switch field IDs in email conditional logic
1855 self::switch_email_condition_field_ids( $new_notification['post_content'] );
1856
1857 // Switch all other field IDs in email
1858 $new_notification['post_content'] = FrmFieldsHelper::switch_field_ids( $new_notification['post_content'] );
1859 }
1860 $new_notification['post_content'] = FrmAppHelper::prepare_and_encode( $new_notification['post_content'] );
1861
1862 $exists = get_posts(
1863 array(
1864 'name' => $new_notification['post_name'],
1865 'post_type' => $new_notification['post_type'],
1866 'post_status' => $new_notification['post_status'],
1867 'numberposts' => 1,
1868 )
1869 );
1870
1871 if ( empty( $exists ) ) {
1872 FrmDb::save_json_post( $new_notification );
1873 ++$imported['imported']['actions'];
1874 }
1875 unset( $new_notification );
1876 }//end foreach
1877
1878 self::remove_deprecated_notification_settings( $form_id, $form_options );
1879 }
1880
1881 /**
1882 * Remove deprecated notification settings after migration
1883 *
1884 * @since 2.05
1885 *
1886 * @param int|string $form_id
1887 * @param array $form_options
1888 */
1889 private static function remove_deprecated_notification_settings( $form_id, $form_options ) {
1890 $delete_settings = array( 'notification', 'autoresponder', 'email_to' );
1891 foreach ( $delete_settings as $index ) {
1892 if ( isset( $form_options[ $index ] ) ) {
1893 unset( $form_options[ $index ] );
1894 }
1895 }
1896 FrmForm::update( $form_id, array( 'options' => $form_options ) );
1897 }
1898
1899 private static function migrate_notifications_to_action( $form_options, $form_id, &$notifications ) {
1900 if ( ! isset( $form_options['notification'] ) && isset( $form_options['email_to'] ) && ! empty( $form_options['email_to'] ) ) {
1901 // add old settings into notification array
1902 $form_options['notification'] = array( 0 => $form_options );
1903 } elseif ( isset( $form_options['notification']['email_to'] ) ) {
1904 // make sure it's in the correct format
1905 $form_options['notification'] = array( 0 => $form_options['notification'] );
1906 }
1907
1908 if ( isset( $form_options['notification'] ) && is_array( $form_options['notification'] ) ) {
1909 foreach ( $form_options['notification'] as $email_key => $notification ) {
1910
1911 $atts = array(
1912 'email_to' => '',
1913 'reply_to' => '',
1914 'reply_to_name' => '',
1915 'event' => '',
1916 'form_id' => $form_id,
1917 'email_key' => $email_key,
1918 );
1919
1920 // Format the email data
1921 self::format_email_data( $atts, $notification );
1922
1923 if ( isset( $notification['twilio'] ) && $notification['twilio'] ) {
1924 do_action( 'frm_create_twilio_action', $atts, $notification );
1925 }
1926
1927 // Setup the new notification
1928 $new_notification = array();
1929 self::setup_new_notification( $new_notification, $notification, $atts );
1930
1931 $notifications[] = $new_notification;
1932 }//end foreach
1933 }//end if
1934 }
1935
1936 private static function format_email_data( &$atts, $notification ) {
1937 // Format email_to
1938 self::format_email_to_data( $atts, $notification );
1939
1940 // Format the reply to email and name
1941 $reply_fields = array(
1942 'reply_to' => '',
1943 'reply_to_name' => '',
1944 );
1945 foreach ( $reply_fields as $f => $val ) {
1946 if ( isset( $notification[ $f ] ) ) {
1947 $atts[ $f ] = $notification[ $f ];
1948 if ( 'custom' == $notification[ $f ] ) {
1949 $atts[ $f ] = $notification[ 'cust_' . $f ];
1950 } elseif ( is_numeric( $atts[ $f ] ) && ! empty( $atts[ $f ] ) ) {
1951 $atts[ $f ] = '[' . $atts[ $f ] . ']';
1952 }
1953 }
1954 unset( $f, $val );
1955 }
1956
1957 // Format event
1958 $atts['event'] = array( 'create' );
1959 if ( isset( $notification['update_email'] ) && 1 == $notification['update_email'] ) {
1960 $atts['event'][] = 'update';
1961 } elseif ( isset( $notification['update_email'] ) && 2 == $notification['update_email'] ) {
1962 $atts['event'] = array( 'update' );
1963 }
1964 }
1965
1966 private static function format_email_to_data( &$atts, $notification ) {
1967 if ( isset( $notification['email_to'] ) ) {
1968 $atts['email_to'] = preg_split( '/ (,|;) /', $notification['email_to'] );
1969 } else {
1970 $atts['email_to'] = array();
1971 }
1972
1973 if ( isset( $notification['also_email_to'] ) ) {
1974 $email_fields = (array) $notification['also_email_to'];
1975 $atts['email_to'] = array_merge( $email_fields, $atts['email_to'] );
1976 unset( $email_fields );
1977 }
1978
1979 foreach ( $atts['email_to'] as $key => $email_field ) {
1980
1981 if ( is_numeric( $email_field ) ) {
1982 $atts['email_to'][ $key ] = '[' . $email_field . ']';
1983 }
1984
1985 if ( strpos( $email_field, '|' ) ) {
1986 $email_opt = explode( '|', $email_field );
1987 if ( isset( $email_opt[0] ) ) {
1988 $atts['email_to'][ $key ] = '[' . $email_opt[0] . ' show=' . $email_opt[1] . ']';
1989 }
1990 unset( $email_opt );
1991 }
1992 }
1993 $atts['email_to'] = implode( ', ', $atts['email_to'] );
1994 }
1995
1996 private static function setup_new_notification( &$new_notification, $notification, $atts ) {
1997 // Set up new notification
1998 $new_notification = array(
1999 'post_content' => array(
2000 'email_to' => $atts['email_to'],
2001 'event' => $atts['event'],
2002 ),
2003 'post_name' => $atts['form_id'] . '_email_' . $atts['email_key'],
2004 );
2005
2006 // Add more fields to the new notification
2007 $add_fields = array( 'email_message', 'email_subject', 'plain_text', 'inc_user_info', 'conditions' );
2008 foreach ( $add_fields as $add_field ) {
2009 if ( isset( $notification[ $add_field ] ) ) {
2010 $new_notification['post_content'][ $add_field ] = $notification[ $add_field ];
2011 } elseif ( in_array( $add_field, array( 'plain_text', 'inc_user_info' ) ) ) {
2012 $new_notification['post_content'][ $add_field ] = 0;
2013 } else {
2014 $new_notification['post_content'][ $add_field ] = '';
2015 }
2016 unset( $add_field );
2017 }
2018
2019 // Set reply to
2020 $new_notification['post_content']['reply_to'] = $atts['reply_to'];
2021
2022 // Set from
2023 if ( ! empty( $atts['reply_to'] ) || ! empty( $atts['reply_to_name'] ) ) {
2024 $new_notification['post_content']['from'] = ( empty( $atts['reply_to_name'] ) ? '[sitename]' : $atts['reply_to_name'] ) . ' <' . ( empty( $atts['reply_to'] ) ? '[admin_email]' : $atts['reply_to'] ) . '>';
2025 }
2026 }
2027
2028 /**
2029 * Switch field IDs in pre-2.0 email conditional logic
2030 *
2031 * @param array $post_content Pass by reference.
2032 */
2033 private static function switch_email_condition_field_ids( &$post_content ) {
2034 // Switch field IDs in conditional logic
2035 if ( isset( $post_content['conditions'] ) && is_array( $post_content['conditions'] ) ) {
2036 foreach ( $post_content['conditions'] as $email_key => $val ) {
2037 if ( is_numeric( $email_key ) ) {
2038 $post_content['conditions'][ $email_key ] = self::switch_action_field_ids( $val, array( 'hide_field' ) );
2039 }
2040 unset( $email_key, $val );
2041 }
2042 }
2043 }
2044
2045 private static function migrate_autoresponder_to_action( $form_options, $form_id, &$notifications ) {
2046 if ( isset( $form_options['auto_responder'] ) && $form_options['auto_responder'] && isset( $form_options['ar_email_message'] ) && $form_options['ar_email_message'] ) {
2047 // migrate autoresponder
2048
2049 $email_field = isset( $form_options['ar_email_to'] ) ? $form_options['ar_email_to'] : 0;
2050 if ( strpos( $email_field, '|' ) ) {
2051 // data from entries field
2052 $email_field = explode( '|', $email_field );
2053 if ( isset( $email_field[1] ) ) {
2054 $email_field = $email_field[1];
2055 }
2056 }
2057 if ( is_numeric( $email_field ) && ! empty( $email_field ) ) {
2058 $email_field = '[' . $email_field . ']';
2059 }
2060
2061 $notification = $form_options;
2062 $new_notification2 = array(
2063 'post_content' => array(
2064 'email_message' => $notification['ar_email_message'],
2065 'email_subject' => isset( $notification['ar_email_subject'] ) ? $notification['ar_email_subject'] : '',
2066 'email_to' => $email_field,
2067 'plain_text' => isset( $notification['ar_plain_text'] ) ? $notification['ar_plain_text'] : 0,
2068 'inc_user_info' => 0,
2069 ),
2070 'post_name' => $form_id . '_email_' . count( $notifications ),
2071 );
2072
2073 $reply_to = isset( $notification['ar_reply_to'] ) ? $notification['ar_reply_to'] : '';
2074 $reply_to_name = isset( $notification['ar_reply_to_name'] ) ? $notification['ar_reply_to_name'] : '';
2075
2076 if ( ! empty( $reply_to ) ) {
2077 $new_notification2['post_content']['reply_to'] = $reply_to;
2078 }
2079
2080 if ( ! empty( $reply_to ) || ! empty( $reply_to_name ) ) {
2081 $new_notification2['post_content']['from'] = ( empty( $reply_to_name ) ? '[sitename]' : $reply_to_name ) . ' <' . ( empty( $reply_to ) ? '[admin_email]' : $reply_to ) . '>';
2082 }
2083
2084 $notifications[] = $new_notification2;
2085 unset( $new_notification2 );
2086 }//end if
2087 }
2088
2089 /**
2090 * PHP 8 backward compatibility for the libxml_disable_entity_loader function
2091 *
2092 * @param boolean $loader
2093 *
2094 * @return boolean
2095 */
2096 public static function maybe_libxml_disable_entity_loader( $loader ) {
2097 if ( version_compare( phpversion(), '8.0', '<' ) && function_exists( 'libxml_disable_entity_loader' ) ) {
2098 $loader = libxml_disable_entity_loader( $loader ); // phpcs:disable Generic.PHP.DeprecatedFunctions.Deprecated
2099 }
2100
2101 return $loader;
2102 }
2103
2104 public static function check_if_libxml_disable_entity_loader_exists() {
2105 return version_compare( phpversion(), '8.0', '<' ) && ! function_exists( 'libxml_disable_entity_loader' );
2106 }
2107
2108 /**
2109 * Get the file types allowed for importing.
2110 * This is used in the "accept" attribute for the file upload input on the XML import page.
2111 *
2112 * @since 6.8.3
2113 *
2114 * @return array
2115 */
2116 public static function get_supported_upload_file_types() {
2117 $file_types = array( '.xml' );
2118 if ( FrmAppHelper::pro_is_installed() ) {
2119 // CSV Importing is only available in Pro.
2120 $file_types[] = '.csv';
2121 }
2122 return $file_types;
2123 }
2124 }
2125