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

2,068 lines 62.2 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 // check for field to edit by field key
451 unset( $f['id'] );
452
453 FrmField::update( $form_fields[ $f['field_key'] ], $f );
454 $imported['updated']['fields'] ++;
455
456 // Unset old field id.
457 unset( $form_fields[ $form_fields[ $f['field_key'] ] ] );
458
459 // Unset old field key.
460 unset( $form_fields[ $f['field_key'] ] );
461 } else {
462 // If no matching field id or key in this form, create the field.
463 self::create_imported_field( $f, $imported );
464 }//end if
465 } else {
466
467 self::create_imported_field( $f, $imported );
468 }//end if
469 }//end foreach
470
471 if ( $keys_by_original_field_id ) {
472 self::maybe_update_field_ids( $form_id, $keys_by_original_field_id );
473 }
474 }
475
476 private static function fill_field( $field, $form_id ) {
477 return array(
478 'id' => (int) $field->id,
479 'field_key' => (string) $field->field_key,
480 'name' => (string) $field->name,
481 'description' => (string) $field->description,
482 'type' => (string) $field->type,
483 'default_value' => FrmAppHelper::maybe_json_decode( (string) $field->default_value ),
484 'field_order' => (int) $field->field_order,
485 'form_id' => (int) $form_id,
486 'required' => (int) $field->required,
487 'options' => FrmAppHelper::maybe_json_decode( (string) $field->options ),
488 'field_options' => FrmAppHelper::maybe_json_decode( (string) $field->field_options ),
489 );
490 }
491
492 /**
493 * @since 4.06
494 */
495 private static function set_default_value( &$f ) {
496 $has_default = array(
497 'text',
498 'email',
499 'url',
500 'textarea',
501 'number',
502 'phone',
503 'date',
504 'hidden',
505 'password',
506 'tag',
507 );
508
509 if ( is_array( $f['default_value'] ) && in_array( $f['type'], $has_default, true ) ) {
510 if ( count( $f['default_value'] ) === 1 ) {
511 $f['default_value'] = '[' . reset( $f['default_value'] ) . ']';
512 } else {
513 $f['default_value'] = reset( $f['default_value'] );
514 }
515 }
516 }
517
518 /**
519 * Make sure the required indicator is set.
520 *
521 * @since 4.05
522 */
523 private static function maybe_add_required( &$f ) {
524 if ( $f['required'] && ! isset( $f['field_options']['required_indicator'] ) ) {
525 $f['field_options']['required_indicator'] = '*';
526 }
527 }
528
529 /**
530 * Update the current in_section value at the beginning of the field loop
531 *
532 * @since 2.0.25
533 * @param int $in_section
534 * @param array $f
535 */
536 private static function maybe_update_in_section_variable( &$in_section, &$f ) {
537 // If we're at the end of a section, switch $in_section is 0
538 if ( in_array( $f['type'], array( 'end_divider', 'break', 'form' ) ) ) {
539 $in_section = 0;
540 }
541
542 // Update the current field's in_section value
543 if ( ! isset( $f['field_options']['in_section'] ) ) {
544 $f['field_options']['in_section'] = $in_section;
545 }
546
547 // If we're starting a new section, switch $in_section to ID of divider
548 if ( $f['type'] == 'divider' ) {
549 $in_section = $f['id'];
550 }
551 }
552
553 /**
554 * Switch the form_select on a repeating field or embedded form if it needs to be switched
555 *
556 * @since 2.0.16
557 *
558 * @param array $f
559 * @param array $imported
560 */
561 private static function maybe_update_form_select( &$f, $imported ) {
562 if ( ! isset( $imported['forms'] ) ) {
563 return;
564 }
565
566 if ( $f['type'] == 'form' || ( $f['type'] == 'divider' && FrmField::is_option_true( $f['field_options'], 'repeat' ) ) ) {
567 if ( FrmField::is_option_true( $f['field_options'], 'form_select' ) ) {
568 $form_select = (int) $f['field_options']['form_select'];
569 if ( isset( $imported['forms'][ $form_select ] ) ) {
570 $f['field_options']['form_select'] = $imported['forms'][ $form_select ];
571 }
572 }
573 }
574 }
575
576 /**
577 * Update the get_values_form setting if the form was imported
578 *
579 * @since 2.01.0
580 *
581 * @param array $imported
582 * @param array $f
583 */
584 private static function maybe_update_get_values_form_setting( $imported, &$f ) {
585 if ( ! isset( $imported['forms'] ) ) {
586 return;
587 }
588
589 if ( FrmField::is_option_true_in_array( $f['field_options'], 'get_values_form' ) ) {
590 $old_form = $f['field_options']['get_values_form'];
591 if ( isset( $imported['forms'][ $old_form ] ) ) {
592 $f['field_options']['get_values_form'] = $imported['forms'][ $old_form ];
593 }
594 }
595 }
596
597 /**
598 * If field settings have been migrated, update the values during import.
599 *
600 * @since 4.0
601 */
602 private static function run_field_migrations( &$f ) {
603 self::migrate_placeholders( $f );
604 $f = apply_filters( 'frm_import_xml_field', $f );
605 }
606
607 /**
608 * @since 4.0
609 */
610 private static function migrate_placeholders( &$f ) {
611 $update_values = self::migrate_field_placeholder( $f, 'clear_on_focus' );
612 foreach ( $update_values as $k => $v ) {
613 $f[ $k ] = $v;
614 }
615
616 $update_values = self::migrate_field_placeholder( $f, 'default_blank' );
617 foreach ( $update_values as $k => $v ) {
618 $f[ $k ] = $v;
619 }
620 }
621
622 /**
623 * Move clear_on_focus or default_blank to placeholder.
624 * Also called during database migration in FrmMigrate.
625 *
626 * @since 4.0
627 * @return array
628 */
629 public static function migrate_field_placeholder( $field, $type ) {
630 $field = (array) $field;
631 $field_options = $field['field_options'];
632 if ( empty( $field_options[ $type ] ) || empty( $field['default_value'] ) ) {
633 return array();
634 }
635
636 $field_options['placeholder'] = is_array( $field['default_value'] ) ? reset( $field['default_value'] ) : $field['default_value'];
637 unset( $field_options['default_blank'], $field_options['clear_on_focus'] );
638
639 $changes = array(
640 'field_options' => $field_options,
641 'default_value' => '',
642 );
643
644 // If a dropdown placeholder was used, remove the option so it won't be included twice.
645 $options = $field['options'];
646 if ( $type === 'default_blank' && is_array( $options ) ) {
647 $default_value = $field['default_value'];
648 if ( is_array( $default_value ) ) {
649 $default_value = reset( $default_value );
650 }
651
652 foreach ( $options as $opt_key => $opt ) {
653 if ( is_array( $opt ) ) {
654 $opt = isset( $opt['value'] ) ? $opt['value'] : ( isset( $opt['label'] ) ? $opt['label'] : reset( $opt ) );
655 }
656
657 if ( $opt == $default_value ) {
658 unset( $options[ $opt_key ] );
659 break;
660 }
661 }
662 $changes['options'] = $options;
663 }
664
665 return $changes;
666 }
667
668 /**
669 * Create an imported field
670 *
671 * @since 2.0.25
672 *
673 * @param array $f
674 * @param array $imported
675 */
676 private static function create_imported_field( $f, &$imported ) {
677 $defaults = self::default_field_options( $f['type'] );
678 $f['field_options'] = array_merge( $defaults, $f['field_options'] );
679
680 if ( is_callable( 'FrmProFileImport::import_attachment' ) ) {
681 $f = self::maybe_import_images_for_options( $f );
682 }
683
684 $new_id = FrmField::create( $f );
685 if ( $new_id != false ) {
686 $imported['imported']['fields'] ++;
687 do_action( 'frm_after_field_is_imported', $f, $new_id );
688 }
689 }
690
691 /**
692 * Import images for radio buttons and checkboxes from image src if available.
693 *
694 * @since 5.5.1
695 *
696 * @param array $field
697 * @return array
698 */
699 private static function maybe_import_images_for_options( $field ) {
700 if ( empty( $field['options'] ) || ! is_array( $field['options'] ) ) {
701 return $field;
702 }
703
704 foreach ( $field['options'] as $key => $option ) {
705 if ( ! is_array( $option ) || empty( $option['src'] ) ) {
706 continue;
707 }
708
709 $field_object = (object) $field;
710 // Fake the file type as FrmProImport::import_attachment checks for file type.
711 $field_object->type = 'file';
712
713 $image_id = FrmProFileImport::import_attachment( $option['src'], $field_object );
714 // Remove the src from options as it isn't required after import.
715 unset( $field['options'][ $key ]['src'] );
716
717 if ( is_numeric( $image_id ) ) {
718 $field['options'][ $key ]['image'] = $image_id;
719 }
720 }
721
722 return $field;
723 }
724
725 /**
726 * Fix field ids for fields that already exist prior to import.
727 *
728 * @since 4.07
729 * @param int $form_id
730 * @param array $keys_by_original_field_id
731 */
732 protected static function maybe_update_field_ids( $form_id, $keys_by_original_field_id ) {
733 global $frm_duplicate_ids;
734
735 $former_duplicate_ids = $frm_duplicate_ids;
736 $where = array(
737 array(
738 'or' => 1,
739 'fi.form_id' => $form_id,
740 'fr.parent_form_id' => $form_id,
741 ),
742 );
743 $fields = FrmField::getAll( $where, 'field_order' );
744 $field_id_by_key = wp_list_pluck( $fields, 'id', 'field_key' );
745
746 foreach ( $fields as $field ) {
747 $before = (array) clone $field;
748 $field = (array) $field;
749 $frm_duplicate_ids = $keys_by_original_field_id;
750 $after = FrmFieldsHelper::switch_field_ids( $field );
751
752 if ( $before['field_options'] !== $after['field_options'] ) {
753 $frm_duplicate_ids = $field_id_by_key;
754 $after = FrmFieldsHelper::switch_field_ids( $after );
755
756 if ( $before['field_options'] !== $after['field_options'] ) {
757 FrmField::update( $field['id'], array( 'field_options' => $after['field_options'] ) );
758 }
759 }
760 }
761
762 $frm_duplicate_ids = $former_duplicate_ids;
763 }
764
765 /**
766 * Updates the custom style setting on import
767 * Convert the post slug to an ID
768 *
769 * @since 2.0.19
770 *
771 * @param array $form
772 */
773 private static function update_custom_style_setting_on_import( &$form ) {
774 if ( ! isset( $form['options']['custom_style'] ) ) {
775 return;
776 }
777
778 if ( is_numeric( $form['options']['custom_style'] ) && 1 === intval( $form['options']['custom_style'] ) ) {
779 // Set to default
780 $form['options']['custom_style'] = 1;
781 } else {
782 // Replace the style name with the style ID on import
783 global $wpdb;
784 $table = $wpdb->prefix . 'posts';
785 $where = array(
786 'post_name' => $form['options']['custom_style'],
787 'post_type' => 'frm_styles',
788 );
789 $select = 'ID';
790 $style_id = FrmDb::get_var( $table, $where, $select );
791
792 if ( $style_id ) {
793 $form['options']['custom_style'] = $style_id;
794 } else {
795 // save the old style to maybe update after styles import
796 $form['options']['old_style'] = $form['options']['custom_style'];
797
798 // Set to default
799 $form['options']['custom_style'] = 1;
800 }
801 }//end if
802 }
803
804 /**
805 * After styles are imported, check for any forms that were linked
806 * and link them back up.
807 *
808 * @since 2.2.7
809 */
810 private static function update_custom_style_setting_after_import( $form_id ) {
811 $form = FrmForm::getOne( $form_id );
812
813 if ( $form && isset( $form->options['old_style'] ) ) {
814 $form = (array) $form;
815 $saved_style = $form['options']['custom_style'];
816 $form['options']['custom_style'] = $form['options']['old_style'];
817 self::update_custom_style_setting_on_import( $form );
818 $has_changed = ( $form['options']['custom_style'] != $saved_style && $form['options']['custom_style'] != $form['options']['old_style'] );
819 if ( $has_changed ) {
820 FrmForm::update( $form['id'], $form );
821 }
822 }
823 }
824
825 public static function import_xml_views( $views, $imported ) {
826 $imported['posts'] = array();
827 $form_action_type = FrmFormActionsController::$action_post_type;
828
829 $post_types = array(
830 'frm_display' => 'views',
831 $form_action_type => 'actions',
832 'frm_styles' => 'styles',
833 );
834
835 $view_ids = array();
836 $posts_with_shortcodes = array();
837
838 foreach ( $views as $item ) {
839 $post = array(
840 'post_title' => (string) $item->title,
841 'post_name' => (string) $item->post_name,
842 'post_type' => (string) $item->post_type,
843 'post_password' => (string) $item->post_password,
844 'guid' => (string) $item->guid,
845 'post_status' => (string) $item->status,
846 'post_author' => FrmAppHelper::get_user_id_param( (string) $item->post_author ),
847 'post_id' => (int) $item->post_id,
848 'post_parent' => (int) $item->post_parent,
849 'menu_order' => (int) $item->menu_order,
850 'post_content' => FrmFieldsHelper::switch_field_ids( (string) $item->content ),
851 'post_excerpt' => FrmFieldsHelper::switch_field_ids( (string) $item->excerpt ),
852 'is_sticky' => (string) $item->is_sticky,
853 'comment_status' => (string) $item->comment_status,
854 'post_date' => (string) $item->post_date,
855 'post_date_gmt' => (string) $item->post_date_gmt,
856 'ping_status' => (string) $item->ping_status,
857 'postmeta' => array(),
858 'layout' => array(),
859 'tax_input' => array(),
860 );
861
862 $post['post_content'] = self::switch_form_ids( $post['post_content'], $imported['forms'] );
863
864 $old_id = $post['post_id'];
865 self::populate_post( $post, $item, $imported );
866
867 unset( $item );
868
869 $post_id = false;
870 if ( $post['post_type'] === $form_action_type ) {
871 $action_control = FrmFormActionsController::get_form_actions( $post['post_excerpt'] );
872 if ( $action_control && is_object( $action_control ) ) {
873 $post_id = $action_control->maybe_create_action( $post, $imported['form_status'] );
874 }
875 unset( $action_control );
876 } elseif ( $post['post_type'] === 'frm_styles' ) {
877 // Properly encode post content before inserting the post
878 $post['post_content'] = FrmAppHelper::maybe_json_decode( $post['post_content'] );
879 $post['post_content'] = FrmAppHelper::prepare_and_encode( $post['post_content'] );
880
881 // Create/update post now
882 $post_id = wp_insert_post( $post );
883 } else {
884 if ( $post['post_type'] === 'frm_display' ) {
885 $post['post_content'] = self::maybe_prepare_json_view_content( $post['post_content'] );
886 } elseif ( 'page' === $post['post_type'] && isset( $imported['posts'][ $post['post_parent'] ] ) ) {
887 $post['post_parent'] = $imported['posts'][ $post['post_parent'] ];
888 }
889 // Create/update post now
890 $post_id = wp_insert_post( $post );
891 }//end if
892
893 if ( ! is_numeric( $post_id ) ) {
894 continue;
895 }
896
897 if ( false !== strpos( $post['post_content'], '[display-frm-data' ) || false !== strpos( $post['post_content'], '[formidable' ) ) {
898 $posts_with_shortcodes[ $post_id ] = $post;
899 }
900
901 self::update_postmeta( $post, $post_id );
902 self::update_layout( $post, $post_id );
903
904 $this_type = 'posts';
905 if ( isset( $post_types[ $post['post_type'] ] ) ) {
906 $this_type = $post_types[ $post['post_type'] ];
907 }
908
909 if ( isset( $post['ID'] ) && $post_id == $post['ID'] ) {
910 $imported['updated'][ $this_type ] ++;
911 } else {
912 $imported['imported'][ $this_type ] ++;
913 }
914
915 $imported['posts'][ (int) $old_id ] = $post_id;
916
917 if ( $post['post_type'] === 'frm_display' ) {
918 $view_ids[ (int) $old_id ] = $post_id;
919 }
920
921 do_action( 'frm_after_import_view', $post_id, $post );
922
923 unset( $post );
924 }//end foreach
925
926 if ( $posts_with_shortcodes && $view_ids ) {
927 self::maybe_switch_view_ids_after_importing_posts( $posts_with_shortcodes, $view_ids );
928 }
929 unset( $posts_with_shortcodes, $view_ids );
930
931 if ( ! empty( $imported['forms'] ) ) {
932 // clear imported forms style cache to make sure the new styles are applied to the forms
933 self::clear_forms_style_caches( $imported['forms'] );
934 }
935
936 self::maybe_update_stylesheet( $imported );
937
938 flush_rewrite_rules();
939
940 return $imported;
941 }
942
943 /**
944 * Clears styles from cache for imported forms
945 *
946 * @param array $imported_forms
947 */
948 private static function clear_forms_style_caches( $imported_forms ) {
949 $where = array(
950 'id' => $imported_forms,
951 'options LIKE' => '"old_style"',
952 );
953 $forms = FrmDb::get_results( 'frm_forms', $where );
954
955 foreach ( $forms as $form ) {
956 FrmAppHelper::unserialize_or_decode( $form->options );
957 if ( ! $form->options ) {
958 continue;
959 }
960 $where = array(
961 'post_name' => $form->options['old_style'],
962 'post_type' => FrmStylesController::$post_type,
963 );
964
965 $select = 'ID';
966
967 $cache_key = FrmDb::generate_cache_key( $where, array( 'limit' => 1 ), $select, 'var' );
968 FrmDb::delete_cache_and_transient( $cache_key, 'post' );
969 }
970 }
971
972 /**
973 * Replace old form ids with new ones in a string.
974 *
975 * @param string $string
976 * @param array<int> $form_ids new form ids indexed by old form id.
977 * @return string
978 */
979 private static function switch_form_ids( $string, $form_ids ) {
980 if ( false === strpos( $string, '[formidable' ) ) {
981 // Skip string replacing if there are no form shortcodes in string.
982 return $string;
983 }
984
985 foreach ( $form_ids as $old_id => $new_id ) {
986 $string = str_replace(
987 array(
988 '[formidable id="' . $old_id . '"',
989 '[formidable id=' . $old_id . ']',
990 '[formidable id=' . $old_id . ' ',
991 '"formId":"' . $old_id . '"',
992 ),
993 array(
994 '[formidable id="' . $new_id . '"',
995 '[formidable id=' . $new_id . ']',
996 '[formidable id=' . $new_id . ' ',
997 '"formId":"' . $new_id . '"',
998 ),
999 $string
1000 );
1001 }
1002
1003 return $string;
1004 }
1005
1006 /**
1007 * @param array<array> $posts_with_shortcodes indexed by current post id.
1008 * @param array<int> $view_ids new view ids indexed by old view id.
1009 * @return void
1010 */
1011 private static function maybe_switch_view_ids_after_importing_posts( $posts_with_shortcodes, $view_ids ) {
1012 foreach ( $posts_with_shortcodes as $imported_post_id => $post ) {
1013 $post_content = self::switch_view_ids( $post['post_content'], $view_ids );
1014 if ( $post_content === $post['post_content'] ) {
1015 continue;
1016 }
1017
1018 wp_update_post(
1019 array(
1020 'ID' => $imported_post_id,
1021 'post_content' => $post_content,
1022 )
1023 );
1024 }
1025 }
1026
1027 /**
1028 * Replace old view ids with new ones in a string.
1029 *
1030 * @param string $string
1031 * @param array<int> $view_ids new view ids indexed by old view id.
1032 * @return string
1033 */
1034 private static function switch_view_ids( $string, $view_ids ) {
1035 if ( false === strpos( $string, '[display-frm-data' ) ) {
1036 // Skip string replacing if there are no view shortcodes in string.
1037 return $string;
1038 }
1039
1040 foreach ( $view_ids as $old_id => $new_id ) {
1041 $string = str_replace(
1042 array(
1043 '[display-frm-data id="' . $old_id . '"',
1044 '[display-frm-data id=' . $old_id . ']',
1045 '[display-frm-data id=' . $old_id . ' ',
1046 '"viewId":"' . $old_id . '"',
1047 ),
1048 array(
1049 '[display-frm-data id="' . $new_id . '"',
1050 '[display-frm-data id=' . $new_id . ']',
1051 '[display-frm-data id=' . $new_id . ' ',
1052 '"viewId":"' . $new_id . '"',
1053 ),
1054 $string
1055 );
1056 unset( $old_id, $new_id );
1057 }
1058
1059 return $string;
1060 }
1061
1062 /**
1063 * @param string $content
1064 * @return string
1065 */
1066 private static function maybe_prepare_json_view_content( $content ) {
1067 $maybe_decoded = FrmAppHelper::maybe_json_decode( $content );
1068 if ( is_array( $maybe_decoded ) && isset( $maybe_decoded[0] ) && isset( $maybe_decoded[0]['box'] ) ) {
1069 return FrmAppHelper::prepare_and_encode( $maybe_decoded );
1070 }
1071 return $content;
1072 }
1073
1074 private static function populate_post( &$post, $item, $imported ) {
1075 if ( isset( $item->attachment_url ) ) {
1076 $post['attachment_url'] = (string) $item->attachment_url;
1077 }
1078
1079 if ( $post['post_type'] == FrmFormActionsController::$action_post_type && isset( $imported['forms'][ (int) $post['menu_order'] ] ) ) {
1080 // update to new form id
1081 $post['menu_order'] = $imported['forms'][ (int) $post['menu_order'] ];
1082 }
1083
1084 // Don't allow default styles to take over a site's default style
1085 if ( 'frm_styles' == $post['post_type'] ) {
1086 $post['menu_order'] = 0;
1087 }
1088
1089 foreach ( $item->postmeta as $meta ) {
1090 self::populate_postmeta( $post, $meta, $imported );
1091 unset( $meta );
1092 }
1093
1094 foreach ( $item->layout as $layout ) {
1095 self::populate_layout( $post, $layout );
1096 unset( $layout );
1097 }
1098
1099 self::populate_taxonomies( $post, $item );
1100
1101 self::maybe_editing_post( $post );
1102 }
1103
1104 /**
1105 * @param array $post
1106 * @param stdClass $meta
1107 * @param array $imported
1108 */
1109 private static function populate_postmeta( &$post, $meta, $imported ) {
1110 global $frm_duplicate_ids;
1111
1112 $m = array(
1113 'key' => (string) $meta->meta_key,
1114 'value' => (string) $meta->meta_value,
1115 );
1116
1117 // Switch old form and field ids to new ones.
1118 if ( 'frm_form_id' === $m['key'] && isset( $imported['forms'][ (int) $m['value'] ] ) ) {
1119 $m['value'] = $imported['forms'][ (int) $m['value'] ];
1120 } else {
1121 $m['value'] = FrmAppHelper::maybe_json_decode( $m['value'] );
1122
1123 if ( ! empty( $frm_duplicate_ids ) ) {
1124 if ( 'frm_dyncontent' === $m['key'] ) {
1125 $m['value'] = self::maybe_prepare_json_view_content( $m['value'] );
1126 $m['value'] = FrmFieldsHelper::switch_field_ids( $m['value'] );
1127 } elseif ( 'frm_options' === $m['key'] ) {
1128
1129 foreach ( array( 'date_field_id', 'edate_field_id' ) as $setting_name ) {
1130 if ( isset( $m['value'][ $setting_name ] ) && is_numeric( $m['value'][ $setting_name ] ) && isset( $frm_duplicate_ids[ $m['value'][ $setting_name ] ] ) ) {
1131 $m['value'][ $setting_name ] = $frm_duplicate_ids[ $m['value'][ $setting_name ] ];
1132 }
1133 }
1134
1135 $check_dup_array = array();
1136 if ( isset( $m['value']['order_by'] ) && ! empty( $m['value']['order_by'] ) ) {
1137 if ( is_numeric( $m['value']['order_by'] ) && isset( $frm_duplicate_ids[ $m['value']['order_by'] ] ) ) {
1138 $m['value']['order_by'] = $frm_duplicate_ids[ $m['value']['order_by'] ];
1139 } elseif ( is_array( $m['value']['order_by'] ) ) {
1140 $check_dup_array[] = 'order_by';
1141 }
1142 }
1143
1144 if ( isset( $m['value']['where'] ) && ! empty( $m['value']['where'] ) ) {
1145 $check_dup_array[] = 'where';
1146 }
1147
1148 foreach ( $check_dup_array as $check_k ) {
1149 foreach ( (array) $m['value'][ $check_k ] as $mk => $mv ) {
1150 if ( isset( $frm_duplicate_ids[ $mv ] ) ) {
1151 $m['value'][ $check_k ][ $mk ] = $frm_duplicate_ids[ $mv ];
1152 }
1153 unset( $mk, $mv );
1154 }
1155 }
1156 }//end if
1157 }//end if
1158 }//end if
1159
1160 if ( ! is_array( $m['value'] ) ) {
1161 $m['value'] = FrmAppHelper::maybe_json_decode( $m['value'] );
1162 }
1163
1164 $post['postmeta'][ (string) $meta->meta_key ] = $m['value'];
1165 }
1166
1167 private static function populate_layout( &$post, $layout ) {
1168 $post['layout'][ (string) $layout->type ] = (string) $layout->data;
1169 }
1170
1171 /**
1172 * Add terms to post
1173 *
1174 * @param array $post By reference.
1175 * @param object $item The XML object data.
1176 */
1177 private static function populate_taxonomies( &$post, $item ) {
1178 foreach ( $item->category as $c ) {
1179 $att = $c->attributes();
1180 if ( ! isset( $att['nicename'] ) ) {
1181 continue;
1182 }
1183
1184 $taxonomy = (string) $att['domain'];
1185 if ( is_taxonomy_hierarchical( $taxonomy ) ) {
1186 $name = (string) $att['nicename'];
1187 $h_term = get_term_by( 'slug', $name, $taxonomy );
1188 if ( $h_term ) {
1189 $name = $h_term->term_id;
1190 }
1191 unset( $h_term );
1192 } else {
1193 $name = (string) $c;
1194 }
1195
1196 if ( ! isset( $post['tax_input'][ $taxonomy ] ) ) {
1197 $post['tax_input'][ $taxonomy ] = array();
1198 }
1199
1200 $post['tax_input'][ $taxonomy ][] = $name;
1201 unset( $name );
1202 }//end foreach
1203 }
1204
1205 /**
1206 * Edit post if the key and created time match
1207 */
1208 private static function maybe_editing_post( &$post ) {
1209 $match_by = array(
1210 'post_type' => $post['post_type'],
1211 'name' => $post['post_name'],
1212 'post_status' => $post['post_status'],
1213 'posts_per_page' => 1,
1214 );
1215
1216 if ( in_array( $post['post_status'], array( 'trash', 'draft' ) ) ) {
1217 $match_by['include'] = $post['post_id'];
1218 unset( $match_by['name'] );
1219 }
1220
1221 $editing = get_posts( $match_by );
1222
1223 if ( ! empty( $editing ) && current( $editing )->post_date == $post['post_date'] ) {
1224 // set the id of the post to edit
1225 $post['ID'] = current( $editing )->ID;
1226 }
1227 }
1228
1229 /**
1230 * @param array $post
1231 * @param int $post_id
1232 * @return void
1233 */
1234 private static function update_postmeta( &$post, $post_id ) {
1235 foreach ( $post['postmeta'] as $k => $v ) {
1236 switch ( $k ) {
1237 case '_edit_last':
1238 $v = FrmAppHelper::get_user_id_param( $v );
1239 break;
1240
1241 case '_thumbnail_id':
1242 if ( FrmAppHelper::pro_is_installed() ) {
1243 // Change the attachment ID.
1244 $field_obj = FrmFieldFactory::get_field_type( 'file' );
1245 $v = $field_obj->get_file_id( $v );
1246 }
1247 break;
1248
1249 case 'frm_dyncontent':
1250 if ( is_array( $v ) ) {
1251 $v = json_encode( $v );
1252 }
1253 break;
1254
1255 case 'frm_param':
1256 add_rewrite_endpoint( $v, EP_PERMALINK | EP_PAGES );
1257 break;
1258 }//end switch
1259
1260 update_post_meta( $post_id, $k, $v );
1261 unset( $k, $v );
1262 }//end foreach
1263 }
1264
1265 /**
1266 * @param array $post
1267 * @param int $post_id
1268 */
1269 private static function update_layout( &$post, $post_id ) {
1270 if ( is_callable( 'FrmViewsLayout::maybe_create_layouts_for_view' ) ) {
1271 $listing_layout = ! empty( $post['layout']['listing'] ) ? json_decode( $post['layout']['listing'], true ) : array();
1272 $detail_layout = ! empty( $post['layout']['detail'] ) ? json_decode( $post['layout']['detail'], true ) : array();
1273 if ( $listing_layout || $detail_layout ) {
1274 FrmViewsLayout::maybe_create_layouts_for_view( $post_id, $listing_layout, $detail_layout );
1275 }
1276 }
1277 }
1278
1279 private static function maybe_update_stylesheet( $imported ) {
1280 $new_styles = isset( $imported['imported']['styles'] ) && ! empty( $imported['imported']['styles'] );
1281 $updated_styles = isset( $imported['updated']['styles'] ) && ! empty( $imported['updated']['styles'] );
1282 if ( $new_styles || $updated_styles ) {
1283 if ( is_admin() && function_exists( 'get_filesystem_method' ) ) {
1284 $frm_style = new FrmStyle();
1285 $frm_style->update( 'default' );
1286 }
1287 foreach ( $imported['forms'] as $form_id ) {
1288 self::update_custom_style_setting_after_import( $form_id );
1289 }
1290 }
1291 }
1292
1293 /**
1294 * @param mixed $result
1295 * @param string $message
1296 * @param array $errors
1297 */
1298 public static function parse_message( $result, &$message, &$errors ) {
1299 if ( is_wp_error( $result ) ) {
1300 $errors[] = $result->get_error_message();
1301
1302 // Remove the SimpleXML_parse_error from the WP_Error object to avoid
1303 // displaying duplicate error messages from $result->get_error_message()
1304 $error_codes = $result->get_error_codes();
1305 $error_details = array();
1306 foreach ( $error_codes as $error_code ) {
1307 // Clone WP_Error data because WP_Error removes all error messages and data
1308 // associated with the specified error code when an item is removed.
1309 // Source: https://developer.wordpress.org/reference/classes/wp_error/remove/#source
1310 $error_details = $result->get_error_data( $error_code );
1311 if ( $error_code === 'SimpleXML_parse_error' ) {
1312 $result->remove( $error_code );
1313 break;
1314 }
1315 }
1316
1317 if ( ! empty( $error_details ) ) {
1318 $errors[] = '<br />' . esc_html_x( 'Error details:', 'import xml message', 'formidable' ) . '<br />' . esc_html( print_r( $error_details, 1 ) );
1319 }
1320
1321 return;
1322 } elseif ( ! $result ) {
1323 return;
1324 }//end if
1325
1326 if ( ! is_array( $result ) ) {
1327 $message = is_string( $result ) ? $result : htmlentities( print_r( $result, 1 ) );
1328
1329 return;
1330 }
1331
1332 $t_strings = array(
1333 'imported' => __( 'Imported', 'formidable' ),
1334 'updated' => __( 'Updated', 'formidable' ),
1335 );
1336
1337 $message = '<ul>';
1338 foreach ( $result as $type => $results ) {
1339 if ( ! isset( $t_strings[ $type ] ) ) {
1340 // only print imported and updated
1341 continue;
1342 }
1343
1344 $s_message = array();
1345 foreach ( $results as $k => $m ) {
1346 self::item_count_message( $m, $k, $s_message );
1347 unset( $k, $m );
1348 }
1349
1350 if ( ! empty( $s_message ) ) {
1351 $message .= '<li><strong>' . $t_strings[ $type ] . ':</strong> ';
1352 $message .= implode( ', ', $s_message );
1353 $message .= '</li>';
1354 }
1355 }
1356
1357 if ( $message == '<ul>' ) {
1358 $message = '';
1359 $errors[] = __( 'Nothing was imported or updated', 'formidable' );
1360 } else {
1361 self::add_form_link_to_message( $result, $message );
1362
1363 /**
1364 * @since 5.3
1365 *
1366 * @param string $message
1367 * @param array $result
1368 */
1369 $message = apply_filters( 'frm_xml_parsed_message', $message, $result );
1370 $message .= '</ul>';
1371 }
1372 }
1373
1374 /**
1375 * @param int $m
1376 * @param string $type
1377 * @param array<string> $s_message
1378 */
1379 public static function item_count_message( $m, $type, &$s_message ) {
1380 if ( ! $m ) {
1381 return;
1382 }
1383
1384 $strings = array(
1385 /* translators: %1$s: Number of items */
1386 'forms' => sprintf( _n( '%1$s Form', '%1$s Forms', $m, 'formidable' ), $m ),
1387 /* translators: %1$s: Number of items */
1388 'fields' => sprintf( _n( '%1$s Field', '%1$s Fields', $m, 'formidable' ), $m ),
1389 /* translators: %1$s: Number of items */
1390 'items' => sprintf( _n( '%1$s Entry', '%1$s Entries', $m, 'formidable' ), $m ),
1391 /* translators: %1$s: Number of items */
1392 'views' => sprintf( _n( '%1$s View', '%1$s Views', $m, 'formidable' ), $m ),
1393 /* translators: %1$s: Number of items */
1394 'posts' => sprintf( _n( '%1$s Page/Post', '%1$s Pages/Posts', $m, 'formidable' ), $m ),
1395 /* translators: %1$s: Number of items */
1396 'styles' => sprintf( _n( '%1$s Style', '%1$s Styles', $m, 'formidable' ), $m ),
1397 /* translators: %1$s: Number of items */
1398 'terms' => sprintf( _n( '%1$s Term', '%1$s Terms', $m, 'formidable' ), $m ),
1399 /* translators: %1$s: Number of items */
1400 'actions' => sprintf( _n( '%1$s Form Action', '%1$s Form Actions', $m, 'formidable' ), $m ),
1401 );
1402
1403 if ( isset( $strings[ $type ] ) ) {
1404 $s_message[] = $strings[ $type ];
1405 } else {
1406 $string = ' ' . $m . ' ' . ucfirst( $type );
1407
1408 /**
1409 * @since 5.3
1410 *
1411 * @param string $string Message string for imported item.
1412 * @param int $m Number of item that was imported.
1413 * }
1414 */
1415 $string = apply_filters( 'frm_xml_' . $type . '_count_message', $string, $m );
1416 $s_message[] = $string;
1417 }
1418 }
1419
1420 /**
1421 * If a single form was imported, include a link in the success message.
1422 *
1423 * @since 4.0
1424 * @param array $result The response from the XML import.
1425 * @param string $message The response shown on the page after import.
1426 */
1427 private static function add_form_link_to_message( $result, &$message ) {
1428 $total_forms = $result['imported']['forms'] + $result['updated']['forms'];
1429 if ( $total_forms > 1 ) {
1430 return;
1431 }
1432
1433 $primary_form = reset( $result['forms'] );
1434 if ( ! empty( $primary_form ) ) {
1435 $primary_form = FrmForm::getOne( $primary_form );
1436 $form_id = empty( $primary_form->parent_form_id ) ? $primary_form->id : $primary_form->parent_form_id;
1437
1438 $message .= '<li><a href="' . esc_url( FrmForm::get_edit_link( $form_id ) ) . '">' . esc_html__( 'Go to imported form', 'formidable' ) . '</a></li>';
1439 }
1440 }
1441
1442 /**
1443 * Prepare the form options for export
1444 *
1445 * @since 2.0.19
1446 *
1447 * @param string $options
1448 *
1449 * @return string
1450 */
1451 public static function prepare_form_options_for_export( $options ) {
1452 FrmAppHelper::unserialize_or_decode( $options );
1453 // Change custom_style to the post_name instead of ID (1 may be a string)
1454 $not_default = isset( $options['custom_style'] ) && 1 != $options['custom_style'];
1455 if ( $not_default ) {
1456 global $wpdb;
1457 $table = $wpdb->prefix . 'posts';
1458 $where = array( 'ID' => $options['custom_style'] );
1459 $select = 'post_name';
1460
1461 $style_name = FrmDb::get_var( $table, $where, $select );
1462
1463 if ( $style_name ) {
1464 $options['custom_style'] = $style_name;
1465 } else {
1466 $options['custom_style'] = 1;
1467 }
1468 }
1469 self::remove_default_form_options( $options );
1470 $options = serialize( $options );
1471
1472 return self::cdata( $options );
1473 }
1474
1475 /**
1476 * If the saved value is the same as the default, remove it from the export
1477 * This keeps file size down and prevents overriding global settings after import
1478 *
1479 * @since 3.06
1480 */
1481 private static function remove_default_form_options( &$options ) {
1482 $defaults = FrmFormsHelper::get_default_opts();
1483 if ( is_callable( 'FrmProFormsHelper::get_default_opts' ) ) {
1484 $defaults += FrmProFormsHelper::get_default_opts();
1485 }
1486 self::remove_defaults( $defaults, $options );
1487 }
1488
1489 /**
1490 * Remove extra settings from field to keep file size down
1491 *
1492 * @since 3.06
1493 */
1494 public static function prepare_field_for_export( &$field ) {
1495 self::remove_default_field_options( $field );
1496 self::add_image_src_to_image_options( $field );
1497 }
1498
1499 /**
1500 * Remove defaults from field options too
1501 *
1502 * @since 3.06
1503 */
1504 private static function remove_default_field_options( &$field ) {
1505 $defaults = self::default_field_options( $field->type );
1506 if ( empty( $defaults['blank'] ) ) {
1507 $global_settings = new FrmSettings();
1508 $global_defaults = $global_settings->default_options();
1509 $defaults['blank'] = $global_defaults['blank_msg'];
1510 }
1511
1512 $options = $field->field_options;
1513 FrmAppHelper::unserialize_or_decode( $options );
1514 self::remove_defaults( $defaults, $options );
1515 self::remove_default_html( 'custom_html', $defaults, $options );
1516
1517 // Get variations on the defaults.
1518 if ( isset( $options['invalid'] ) ) {
1519 $defaults = array(
1520 /* translators: %s: Field name */
1521 'invalid' => sprintf( __( '%s is invalid', 'formidable' ), $field->name ),
1522 );
1523 self::remove_defaults( $defaults, $options );
1524 }
1525
1526 $field->field_options = serialize( $options );
1527 }
1528
1529 /**
1530 * Add image "src" key to each image option so the image can be imported to another website.
1531 *
1532 * @since 5.5.1
1533 *
1534 * @param stdClass $field
1535 * @return void
1536 */
1537 private static function add_image_src_to_image_options( $field ) {
1538 if ( empty( $field->options ) || false === strpos( $field->options, 'image' ) ) {
1539 return;
1540 }
1541
1542 $updated = false;
1543 $options = $field->options;
1544 FrmAppHelper::unserialize_or_decode( $options );
1545
1546 if ( ! $options || ! is_array( $options ) ) {
1547 return;
1548 }
1549
1550 foreach ( $options as $key => $option ) {
1551 if ( is_array( $option ) && ! empty( $option['image'] ) ) {
1552 $options[ $key ]['src'] = wp_get_attachment_url( $option['image'] );
1553 $updated = true;
1554 }
1555 }
1556
1557 if ( $updated ) {
1558 $field->options = maybe_serialize( $options );
1559 }
1560 }
1561
1562 /**
1563 * @since 3.06.03
1564 */
1565 private static function default_field_options( $type ) {
1566 $defaults = FrmFieldsHelper::get_default_field_options( $type );
1567 if ( empty( $defaults['custom_html'] ) ) {
1568 $defaults['custom_html'] = FrmFieldsHelper::get_default_html( $type );
1569 }
1570 return $defaults;
1571 }
1572
1573 /**
1574 * Compare the default array to the saved values and
1575 * remove if they are the same
1576 *
1577 * @since 3.06
1578 */
1579 private static function remove_defaults( $defaults, &$saved ) {
1580 foreach ( $saved as $key => $value ) {
1581 if ( isset( $defaults[ $key ] ) && $defaults[ $key ] === $value ) {
1582 unset( $saved[ $key ] );
1583 }
1584 }
1585 }
1586
1587 /**
1588 * The line endings may prevent html from being equal when it should
1589 *
1590 * @since 3.06
1591 */
1592 private static function remove_default_html( $html_name, $defaults, &$options ) {
1593 if ( ! isset( $options[ $html_name ] ) || ! isset( $defaults[ $html_name ] ) ) {
1594 return;
1595 }
1596
1597 $old_html = str_replace( "\r\n", "\n", $options[ $html_name ] );
1598 $default_html = $defaults[ $html_name ];
1599 if ( $old_html == $default_html ) {
1600 unset( $options[ $html_name ] );
1601
1602 return;
1603 }
1604
1605 // Account for some of the older field default HTML.
1606 $default_html = str_replace( ' id="frm_desc_field_[key]"', '', $default_html );
1607 if ( $old_html == $default_html ) {
1608 unset( $options[ $html_name ] );
1609 }
1610 }
1611
1612 public static function cdata( $str ) {
1613 FrmAppHelper::unserialize_or_decode( $str );
1614 if ( is_array( $str ) ) {
1615 $str = json_encode( $str );
1616 } elseif ( seems_utf8( $str ) === false ) {
1617 $str = FrmAppHelper::maybe_utf8_encode( $str );
1618 }
1619
1620 if ( is_numeric( $str ) ) {
1621 return $str;
1622 }
1623
1624 self::remove_invalid_characters_from_xml( $str );
1625
1626 // $str = ent2ncr(esc_html( $str));
1627 $str = '<![CDATA[' . str_replace( ']]>', ']]]]><![CDATA[>', $str ) . ']]>';
1628
1629 return $str;
1630 }
1631
1632 /**
1633 * Remove <US> character (unit separator) from exported strings
1634 *
1635 * @since 2.0.22
1636 *
1637 * @param string $str
1638 */
1639 private static function remove_invalid_characters_from_xml( &$str ) {
1640 // Remove <US> character
1641 $str = str_replace( '\x1F', '', $str );
1642 }
1643
1644 public static function migrate_form_settings_to_actions( $form_options, $form_id, &$imported = array(), $switch = false ) {
1645 // Get post type
1646 $post_type = FrmFormActionsController::$action_post_type;
1647
1648 // Set up imported index, if not set up yet
1649 if ( ! isset( $imported['imported']['actions'] ) ) {
1650 $imported['imported']['actions'] = 0;
1651 }
1652
1653 // Migrate post settings to action
1654 self::migrate_post_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1655
1656 // Migrate email settings to action
1657 self::migrate_email_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1658 }
1659
1660 /**
1661 * Migrate post settings to form action
1662 *
1663 * @param array $form_options
1664 * @param int $form_id
1665 * @param string $post_type
1666 * @param array $imported
1667 * @param bool $switch
1668 */
1669 private static function migrate_post_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1670 if ( ! isset( $form_options['create_post'] ) || ! $form_options['create_post'] ) {
1671 return;
1672 }
1673
1674 $new_action = array(
1675 'post_type' => $post_type,
1676 'post_excerpt' => 'wppost',
1677 'post_title' => __( 'Create Posts', 'formidable' ),
1678 'menu_order' => $form_id,
1679 'post_status' => 'publish',
1680 'post_content' => array(),
1681 'post_name' => $form_id . '_wppost_1',
1682 );
1683
1684 $post_settings = array(
1685 'post_type',
1686 'post_category',
1687 'post_content',
1688 'post_excerpt',
1689 'post_title',
1690 'post_name',
1691 'post_date',
1692 'post_status',
1693 'post_custom_fields',
1694 'post_password',
1695 'post_parent',
1696 );
1697
1698 foreach ( $post_settings as $post_setting ) {
1699 if ( isset( $form_options[ $post_setting ] ) ) {
1700 $new_action['post_content'][ $post_setting ] = $form_options[ $post_setting ];
1701 }
1702 unset( $post_setting );
1703 }
1704
1705 $new_action['event'] = array( 'create', 'update' );
1706
1707 if ( $switch ) {
1708 // Fields with string or int saved.
1709 $basic_fields = array(
1710 'post_title',
1711 'post_content',
1712 'post_excerpt',
1713 'post_password',
1714 'post_date',
1715 'post_status',
1716 'post_parent',
1717 );
1718
1719 // Fields with arrays saved.
1720 $array_fields = array( 'post_category', 'post_custom_fields' );
1721
1722 $new_action['post_content'] = self::switch_action_field_ids( $new_action['post_content'], $basic_fields, $array_fields );
1723 }
1724 $new_action['post_content'] = json_encode( $new_action['post_content'] );
1725
1726 $exists = get_posts(
1727 array(
1728 'name' => $new_action['post_name'],
1729 'post_type' => $new_action['post_type'],
1730 'post_status' => $new_action['post_status'],
1731 'numberposts' => 1,
1732 )
1733 );
1734
1735 if ( ! $exists ) {
1736 // this isn't an email, but we need to use a class that will always be included
1737 FrmDb::save_json_post( $new_action );
1738 $imported['imported']['actions'] ++;
1739 }
1740 }
1741
1742 /**
1743 * Switch old field IDs for new field IDs in emails and post
1744 *
1745 * @since 2.0
1746 *
1747 * @param array $post_content Check for old field IDs.
1748 * @param array $basic_fields Fields with string or int saved.
1749 * @param array $array_fields Fields with arrays saved.
1750 *
1751 * @return string $post_content - new field IDs
1752 */
1753 private static function switch_action_field_ids( $post_content, $basic_fields, $array_fields = array() ) {
1754 global $frm_duplicate_ids;
1755
1756 // If there aren't IDs that were switched, end now
1757 if ( ! $frm_duplicate_ids ) {
1758 return;
1759 }
1760
1761 // Get old IDs
1762 $old = array_keys( $frm_duplicate_ids );
1763
1764 // Get new IDs
1765 $new = array_values( $frm_duplicate_ids );
1766
1767 // Do a str_replace with each item to set the new IDs
1768 foreach ( $post_content as $key => $setting ) {
1769 if ( ! is_array( $setting ) && in_array( $key, $basic_fields ) ) {
1770 // Replace old IDs with new IDs
1771 $post_content[ $key ] = str_replace( $old, $new, $setting );
1772 } elseif ( is_array( $setting ) && in_array( $key, $array_fields ) ) {
1773 foreach ( $setting as $k => $val ) {
1774 // Replace old IDs with new IDs
1775 $post_content[ $key ][ $k ] = str_replace( $old, $new, $val );
1776 }
1777 }
1778 unset( $key, $setting );
1779 }
1780
1781 return $post_content;
1782 }
1783
1784 private static function migrate_email_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1785 // No old notifications or autoresponders to carry over
1786 if ( ! isset( $form_options['auto_responder'] ) && ! isset( $form_options['notification'] ) && ! isset( $form_options['email_to'] ) ) {
1787 return;
1788 }
1789
1790 // Initialize notifications array
1791 $notifications = array();
1792
1793 // Migrate regular notifications
1794 self::migrate_notifications_to_action( $form_options, $form_id, $notifications );
1795
1796 // Migrate autoresponders
1797 self::migrate_autoresponder_to_action( $form_options, $form_id, $notifications );
1798
1799 if ( empty( $notifications ) ) {
1800 return;
1801 }
1802
1803 foreach ( $notifications as $new_notification ) {
1804 $new_notification['post_type'] = $post_type;
1805 $new_notification['post_excerpt'] = 'email';
1806 $new_notification['post_title'] = __( 'Email Notification', 'formidable' );
1807 $new_notification['menu_order'] = $form_id;
1808 $new_notification['post_status'] = 'publish';
1809
1810 // Switch field IDs and keys, if needed
1811 if ( $switch ) {
1812
1813 // Switch field IDs in email conditional logic
1814 self::switch_email_condition_field_ids( $new_notification['post_content'] );
1815
1816 // Switch all other field IDs in email
1817 $new_notification['post_content'] = FrmFieldsHelper::switch_field_ids( $new_notification['post_content'] );
1818 }
1819 $new_notification['post_content'] = FrmAppHelper::prepare_and_encode( $new_notification['post_content'] );
1820
1821 $exists = get_posts(
1822 array(
1823 'name' => $new_notification['post_name'],
1824 'post_type' => $new_notification['post_type'],
1825 'post_status' => $new_notification['post_status'],
1826 'numberposts' => 1,
1827 )
1828 );
1829
1830 if ( empty( $exists ) ) {
1831 FrmDb::save_json_post( $new_notification );
1832 $imported['imported']['actions'] ++;
1833 }
1834 unset( $new_notification );
1835 }//end foreach
1836
1837 self::remove_deprecated_notification_settings( $form_id, $form_options );
1838 }
1839
1840 /**
1841 * Remove deprecated notification settings after migration
1842 *
1843 * @since 2.05
1844 *
1845 * @param int|string $form_id
1846 * @param array $form_options
1847 */
1848 private static function remove_deprecated_notification_settings( $form_id, $form_options ) {
1849 $delete_settings = array( 'notification', 'autoresponder', 'email_to' );
1850 foreach ( $delete_settings as $index ) {
1851 if ( isset( $form_options[ $index ] ) ) {
1852 unset( $form_options[ $index ] );
1853 }
1854 }
1855 FrmForm::update( $form_id, array( 'options' => $form_options ) );
1856 }
1857
1858 private static function migrate_notifications_to_action( $form_options, $form_id, &$notifications ) {
1859 if ( ! isset( $form_options['notification'] ) && isset( $form_options['email_to'] ) && ! empty( $form_options['email_to'] ) ) {
1860 // add old settings into notification array
1861 $form_options['notification'] = array( 0 => $form_options );
1862 } elseif ( isset( $form_options['notification']['email_to'] ) ) {
1863 // make sure it's in the correct format
1864 $form_options['notification'] = array( 0 => $form_options['notification'] );
1865 }
1866
1867 if ( isset( $form_options['notification'] ) && is_array( $form_options['notification'] ) ) {
1868 foreach ( $form_options['notification'] as $email_key => $notification ) {
1869
1870 $atts = array(
1871 'email_to' => '',
1872 'reply_to' => '',
1873 'reply_to_name' => '',
1874 'event' => '',
1875 'form_id' => $form_id,
1876 'email_key' => $email_key,
1877 );
1878
1879 // Format the email data
1880 self::format_email_data( $atts, $notification );
1881
1882 if ( isset( $notification['twilio'] ) && $notification['twilio'] ) {
1883 do_action( 'frm_create_twilio_action', $atts, $notification );
1884 }
1885
1886 // Setup the new notification
1887 $new_notification = array();
1888 self::setup_new_notification( $new_notification, $notification, $atts );
1889
1890 $notifications[] = $new_notification;
1891 }//end foreach
1892 }//end if
1893 }
1894
1895 private static function format_email_data( &$atts, $notification ) {
1896 // Format email_to
1897 self::format_email_to_data( $atts, $notification );
1898
1899 // Format the reply to email and name
1900 $reply_fields = array(
1901 'reply_to' => '',
1902 'reply_to_name' => '',
1903 );
1904 foreach ( $reply_fields as $f => $val ) {
1905 if ( isset( $notification[ $f ] ) ) {
1906 $atts[ $f ] = $notification[ $f ];
1907 if ( 'custom' == $notification[ $f ] ) {
1908 $atts[ $f ] = $notification[ 'cust_' . $f ];
1909 } elseif ( is_numeric( $atts[ $f ] ) && ! empty( $atts[ $f ] ) ) {
1910 $atts[ $f ] = '[' . $atts[ $f ] . ']';
1911 }
1912 }
1913 unset( $f, $val );
1914 }
1915
1916 // Format event
1917 $atts['event'] = array( 'create' );
1918 if ( isset( $notification['update_email'] ) && 1 == $notification['update_email'] ) {
1919 $atts['event'][] = 'update';
1920 } elseif ( isset( $notification['update_email'] ) && 2 == $notification['update_email'] ) {
1921 $atts['event'] = array( 'update' );
1922 }
1923 }
1924
1925 private static function format_email_to_data( &$atts, $notification ) {
1926 if ( isset( $notification['email_to'] ) ) {
1927 $atts['email_to'] = preg_split( '/ (,|;) /', $notification['email_to'] );
1928 } else {
1929 $atts['email_to'] = array();
1930 }
1931
1932 if ( isset( $notification['also_email_to'] ) ) {
1933 $email_fields = (array) $notification['also_email_to'];
1934 $atts['email_to'] = array_merge( $email_fields, $atts['email_to'] );
1935 unset( $email_fields );
1936 }
1937
1938 foreach ( $atts['email_to'] as $key => $email_field ) {
1939
1940 if ( is_numeric( $email_field ) ) {
1941 $atts['email_to'][ $key ] = '[' . $email_field . ']';
1942 }
1943
1944 if ( strpos( $email_field, '|' ) ) {
1945 $email_opt = explode( '|', $email_field );
1946 if ( isset( $email_opt[0] ) ) {
1947 $atts['email_to'][ $key ] = '[' . $email_opt[0] . ' show=' . $email_opt[1] . ']';
1948 }
1949 unset( $email_opt );
1950 }
1951 }
1952 $atts['email_to'] = implode( ', ', $atts['email_to'] );
1953 }
1954
1955 private static function setup_new_notification( &$new_notification, $notification, $atts ) {
1956 // Set up new notification
1957 $new_notification = array(
1958 'post_content' => array(
1959 'email_to' => $atts['email_to'],
1960 'event' => $atts['event'],
1961 ),
1962 'post_name' => $atts['form_id'] . '_email_' . $atts['email_key'],
1963 );
1964
1965 // Add more fields to the new notification
1966 $add_fields = array( 'email_message', 'email_subject', 'plain_text', 'inc_user_info', 'conditions' );
1967 foreach ( $add_fields as $add_field ) {
1968 if ( isset( $notification[ $add_field ] ) ) {
1969 $new_notification['post_content'][ $add_field ] = $notification[ $add_field ];
1970 } elseif ( in_array( $add_field, array( 'plain_text', 'inc_user_info' ) ) ) {
1971 $new_notification['post_content'][ $add_field ] = 0;
1972 } else {
1973 $new_notification['post_content'][ $add_field ] = '';
1974 }
1975 unset( $add_field );
1976 }
1977
1978 // Set reply to
1979 $new_notification['post_content']['reply_to'] = $atts['reply_to'];
1980
1981 // Set from
1982 if ( ! empty( $atts['reply_to'] ) || ! empty( $atts['reply_to_name'] ) ) {
1983 $new_notification['post_content']['from'] = ( empty( $atts['reply_to_name'] ) ? '[sitename]' : $atts['reply_to_name'] ) . ' <' . ( empty( $atts['reply_to'] ) ? '[admin_email]' : $atts['reply_to'] ) . '>';
1984 }
1985 }
1986
1987 /**
1988 * Switch field IDs in pre-2.0 email conditional logic
1989 *
1990 * @param array $post_content Pass by reference.
1991 */
1992 private static function switch_email_condition_field_ids( &$post_content ) {
1993 // Switch field IDs in conditional logic
1994 if ( isset( $post_content['conditions'] ) && is_array( $post_content['conditions'] ) ) {
1995 foreach ( $post_content['conditions'] as $email_key => $val ) {
1996 if ( is_numeric( $email_key ) ) {
1997 $post_content['conditions'][ $email_key ] = self::switch_action_field_ids( $val, array( 'hide_field' ) );
1998 }
1999 unset( $email_key, $val );
2000 }
2001 }
2002 }
2003
2004 private static function migrate_autoresponder_to_action( $form_options, $form_id, &$notifications ) {
2005 if ( isset( $form_options['auto_responder'] ) && $form_options['auto_responder'] && isset( $form_options['ar_email_message'] ) && $form_options['ar_email_message'] ) {
2006 // migrate autoresponder
2007
2008 $email_field = isset( $form_options['ar_email_to'] ) ? $form_options['ar_email_to'] : 0;
2009 if ( strpos( $email_field, '|' ) ) {
2010 // data from entries field
2011 $email_field = explode( '|', $email_field );
2012 if ( isset( $email_field[1] ) ) {
2013 $email_field = $email_field[1];
2014 }
2015 }
2016 if ( is_numeric( $email_field ) && ! empty( $email_field ) ) {
2017 $email_field = '[' . $email_field . ']';
2018 }
2019
2020 $notification = $form_options;
2021 $new_notification2 = array(
2022 'post_content' => array(
2023 'email_message' => $notification['ar_email_message'],
2024 'email_subject' => isset( $notification['ar_email_subject'] ) ? $notification['ar_email_subject'] : '',
2025 'email_to' => $email_field,
2026 'plain_text' => isset( $notification['ar_plain_text'] ) ? $notification['ar_plain_text'] : 0,
2027 'inc_user_info' => 0,
2028 ),
2029 'post_name' => $form_id . '_email_' . count( $notifications ),
2030 );
2031
2032 $reply_to = isset( $notification['ar_reply_to'] ) ? $notification['ar_reply_to'] : '';
2033 $reply_to_name = isset( $notification['ar_reply_to_name'] ) ? $notification['ar_reply_to_name'] : '';
2034
2035 if ( ! empty( $reply_to ) ) {
2036 $new_notification2['post_content']['reply_to'] = $reply_to;
2037 }
2038
2039 if ( ! empty( $reply_to ) || ! empty( $reply_to_name ) ) {
2040 $new_notification2['post_content']['from'] = ( empty( $reply_to_name ) ? '[sitename]' : $reply_to_name ) . ' <' . ( empty( $reply_to ) ? '[admin_email]' : $reply_to ) . '>';
2041 }
2042
2043 $notifications[] = $new_notification2;
2044 unset( $new_notification2 );
2045 }//end if
2046 }
2047
2048 /**
2049 * PHP 8 backward compatibility for the libxml_disable_entity_loader function
2050 *
2051 * @param boolean $loader
2052 *
2053 * @return boolean
2054 */
2055 public static function maybe_libxml_disable_entity_loader( $loader ) {
2056 if ( version_compare( phpversion(), '8.0', '<' ) && function_exists( 'libxml_disable_entity_loader' ) ) {
2057 $loader = libxml_disable_entity_loader( $loader ); // phpcs:disable Generic.PHP.DeprecatedFunctions.Deprecated
2058 }
2059
2060 return $loader;
2061 }
2062
2063 public static function check_if_libxml_disable_entity_loader_exists() {
2064 return version_compare( phpversion(), '8.0', '<' ) && ! function_exists( 'libxml_disable_entity_loader' );
2065 }
2066 }
2067
2068