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

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