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

1,693 lines 50.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 public static function get_xml_values( $opt, $padding ) {
9 if ( is_array( $opt ) ) {
10 foreach ( $opt as $ok => $ov ) {
11 echo "\n" . esc_html( $padding );
12 $tag = ( is_numeric( $ok ) ? 'key:' : '' ) . $ok;
13 echo '<' . esc_html( $tag ) . '>';
14 self::get_xml_values( $ov, $padding . ' ' );
15 if ( is_array( $ov ) ) {
16 echo "\n" . esc_html( $padding );
17 }
18 echo '</' . esc_html( $tag ) . '>';
19 }
20 } else {
21 echo self::cdata( $opt ); // WPCS: XSS ok.
22 }
23 }
24
25 public static function import_xml( $file ) {
26 if ( ! defined( 'WP_IMPORTING' ) ) {
27 define( 'WP_IMPORTING', true );
28 }
29
30 if ( ! class_exists( 'DOMDocument' ) ) {
31 return new WP_Error( 'SimpleXML_parse_error', __( 'Your server does not have XML enabled', 'formidable' ), libxml_get_errors() );
32 }
33
34 $dom = new DOMDocument();
35 $success = $dom->loadXML( file_get_contents( $file ) );
36 if ( ! $success ) {
37 return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this XML file', 'formidable' ), libxml_get_errors() );
38 }
39
40 if ( ! function_exists( 'simplexml_import_dom' ) ) {
41 return new WP_Error( 'SimpleXML_parse_error', __( 'Your server is missing the simplexml_import_dom function', 'formidable' ), libxml_get_errors() );
42 }
43
44 $xml = simplexml_import_dom( $dom );
45 unset( $dom );
46
47 // halt if loading produces an error
48 if ( ! $xml ) {
49 return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this XML file', 'formidable' ), libxml_get_errors() );
50 }
51
52 return self::import_xml_now( $xml );
53 }
54
55 /**
56 * Add terms, forms (form and field ids), posts (post ids), and entries to db, in that order
57 *
58 * @since 3.06
59 * @return array The number of items imported
60 */
61 public static function import_xml_now( $xml ) {
62 if ( ! defined( 'WP_IMPORTING' ) ) {
63 define( 'WP_IMPORTING', true );
64 }
65
66 $imported = self::pre_import_data();
67
68 foreach ( array( 'term', 'form', 'view' ) as $item_type ) {
69 // Grab cats, tags, and terms, or forms or posts.
70 if ( isset( $xml->{$item_type} ) ) {
71 $function_name = 'import_xml_' . $item_type . 's';
72 $imported = self::$function_name( $xml->{$item_type}, $imported );
73 unset( $function_name, $xml->{$item_type} );
74 }
75 }
76
77 $imported = apply_filters( 'frm_importing_xml', $imported, $xml );
78
79 if ( ! isset( $imported['form_status'] ) || empty( $imported['form_status'] ) ) {
80 // Check for an error message in the XML.
81 if ( isset( $xml->Code ) && isset( $xml->Message ) ) { // phpcs:ignore WordPress.NamingConventions
82 $imported['error'] = reset( $xml->Message ); // phpcs:ignore WordPress.NamingConventions
83 }
84 }
85
86 return $imported;
87 }
88
89 /**
90 * @since 3.06
91 * @return array
92 */
93 private static function pre_import_data() {
94 $defaults = array(
95 'forms' => 0,
96 'fields' => 0,
97 'terms' => 0,
98 'posts' => 0,
99 'views' => 0,
100 'actions' => 0,
101 'styles' => 0,
102 );
103
104 return array(
105 'imported' => $defaults,
106 'updated' => $defaults,
107 'forms' => array(),
108 'terms' => array(),
109 );
110 }
111
112 public static function import_xml_terms( $terms, $imported ) {
113 foreach ( $terms as $t ) {
114 if ( term_exists( (string) $t->term_slug, (string) $t->term_taxonomy ) ) {
115 continue;
116 }
117
118 $parent = self::get_term_parent_id( $t );
119
120 $term = wp_insert_term(
121 (string) $t->term_name,
122 (string) $t->term_taxonomy,
123 array(
124 'slug' => (string) $t->term_slug,
125 'description' => (string) $t->term_description,
126 'parent' => empty( $parent ) ? 0 : $parent,
127 )
128 );
129
130 if ( $term && is_array( $term ) ) {
131 $imported['imported']['terms'] ++;
132 $imported['terms'][ (int) $t->term_id ] = $term['term_id'];
133 }
134
135 unset( $term, $t );
136 }
137
138 return $imported;
139 }
140
141 /**
142 * @since 2.0.8
143 */
144 private static function get_term_parent_id( $t ) {
145 $parent = (string) $t->term_parent;
146 if ( ! empty( $parent ) ) {
147 $parent = term_exists( (string) $t->term_parent, (string) $t->term_taxonomy );
148 if ( $parent ) {
149 $parent = $parent['term_id'];
150 } else {
151 $parent = 0;
152 }
153 }
154
155 return $parent;
156 }
157
158 public static function import_xml_forms( $forms, $imported ) {
159 $child_forms = array();
160
161 // Import child forms first
162 self::put_child_forms_first( $forms );
163
164 foreach ( $forms as $item ) {
165 $form = self::fill_form( $item );
166
167 self::update_custom_style_setting_on_import( $form );
168
169 $this_form = self::maybe_get_form( $form );
170
171 $old_id = false;
172 $form_fields = false;
173 if ( ! empty( $this_form ) ) {
174 $form_id = $this_form->id;
175 $old_id = $this_form->id;
176 self::update_form( $this_form, $form, $imported );
177
178 $form_fields = self::get_form_fields( $form_id );
179 } else {
180 $form_id = FrmForm::create( $form );
181 if ( $form_id ) {
182 if ( empty( $form['parent_form_id'] ) ) {
183 // Don't include the repeater form in the imported count.
184 $imported['imported']['forms'] ++;
185 }
186
187 // Keep track of whether this specific form was updated or not.
188 $imported['form_status'][ $form_id ] = 'imported';
189 }
190 }
191
192 if ( $form_id ) {
193 self::track_imported_child_forms( (int) $form_id, $form['parent_form_id'], $child_forms );
194 }
195
196 self::import_xml_fields( $item->field, $form_id, $this_form, $form_fields, $imported );
197
198 self::delete_removed_fields( $form_fields );
199
200 // Update field ids/keys to new ones.
201 do_action( 'frm_after_duplicate_form', $form_id, $form, array( 'old_id' => $old_id ) );
202
203 $imported['forms'][ (int) $item->id ] = $form_id;
204
205 // Send pre 2.0 form options through function that creates actions.
206 self::migrate_form_settings_to_actions( $form['options'], $form_id, $imported, true );
207
208 do_action( 'frm_after_import_form', $form_id, $form );
209
210 unset( $form, $item );
211 }
212
213 self::maybe_update_child_form_parent_id( $imported['forms'], $child_forms );
214
215 return $imported;
216 }
217
218 private static function fill_form( $item ) {
219 $form = array(
220 'id' => (int) $item->id,
221 'form_key' => (string) $item->form_key,
222 'name' => (string) $item->name,
223 'description' => (string) $item->description,
224 'options' => (string) $item->options,
225 'logged_in' => (int) $item->logged_in,
226 'is_template' => (int) $item->is_template,
227 'editable' => (int) $item->editable,
228 'status' => (string) $item->status,
229 'parent_form_id' => isset( $item->parent_form_id ) ? (int) $item->parent_form_id : 0,
230 'created_at' => gmdate( 'Y-m-d H:i:s', strtotime( (string) $item->created_at ) ),
231 );
232
233 if ( empty( $item->created_at ) ) {
234 $form['created_at'] = current_time( 'mysql', 1 );
235 }
236
237 $form['options'] = FrmAppHelper::maybe_json_decode( $form['options'] );
238
239 return $form;
240 }
241
242 private static function maybe_get_form( $form ) {
243 // if template, allow to edit if form keys match, otherwise, creation date must also match
244 $edit_query = array(
245 'form_key' => $form['form_key'],
246 'is_template' => $form['is_template'],
247 );
248 if ( ! $form['is_template'] ) {
249 $edit_query['created_at'] = $form['created_at'];
250 }
251
252 $edit_query = apply_filters( 'frm_match_xml_form', $edit_query, $form );
253
254 return FrmForm::getAll( $edit_query, '', 1 );
255 }
256
257 private static function update_form( $this_form, $form, &$imported ) {
258 $form_id = $this_form->id;
259 FrmForm::update( $form_id, $form );
260 if ( empty( $form['parent_form_id'] ) ) {
261 // Don't include the repeater form in the updated count.
262 $imported['updated']['forms'] ++;
263 }
264
265 // Keep track of whether this specific form was updated or not
266 $imported['form_status'][ $form_id ] = 'updated';
267 }
268
269 private static function get_form_fields( $form_id ) {
270 $form_fields = FrmField::get_all_for_form( $form_id, '', 'exclude', 'exclude' );
271 $old_fields = array();
272 foreach ( $form_fields as $f ) {
273 $old_fields[ $f->id ] = $f;
274 $old_fields[ $f->field_key ] = $f->id;
275 unset( $f );
276 }
277 $form_fields = $old_fields;
278
279 return $form_fields;
280 }
281
282 /**
283 * Delete any fields attached to this form that were not included in the template
284 */
285 private static function delete_removed_fields( $form_fields ) {
286 if ( ! empty( $form_fields ) ) {
287 foreach ( $form_fields as $field ) {
288 if ( is_object( $field ) ) {
289 FrmField::destroy( $field->id );
290 }
291 unset( $field );
292 }
293 }
294 }
295
296 /**
297 * Put child forms first so they will be imported before parents
298 *
299 * @since 2.0.16
300 *
301 * @param array $forms
302 */
303 private static function put_child_forms_first( &$forms ) {
304 $child_forms = array();
305 $regular_forms = array();
306
307 foreach ( $forms as $form ) {
308 $parent_form_id = isset( $form->parent_form_id ) ? (int) $form->parent_form_id : 0;
309
310 if ( $parent_form_id ) {
311 $child_forms[] = $form;
312 } else {
313 $regular_forms[] = $form;
314 }
315 }
316
317 $forms = array_merge( $child_forms, $regular_forms );
318 }
319
320 /**
321 * Keep track of all imported child forms
322 *
323 * @since 2.0.16
324 *
325 * @param int $form_id
326 * @param int $parent_form_id
327 * @param array $child_forms
328 */
329 private static function track_imported_child_forms( $form_id, $parent_form_id, &$child_forms ) {
330 if ( $parent_form_id ) {
331 $child_forms[ $form_id ] = $parent_form_id;
332 }
333 }
334
335 /**
336 * Update the parent_form_id on imported child forms
337 * Child forms are imported first so their parent_form_id will need to be updated after the parent is imported
338 *
339 * @since 2.0.6
340 *
341 * @param array $imported_forms
342 * @param array $child_forms
343 */
344 private static function maybe_update_child_form_parent_id( $imported_forms, $child_forms ) {
345 foreach ( $child_forms as $child_form_id => $old_parent_form_id ) {
346 if ( isset( $imported_forms[ $old_parent_form_id ] ) && (int) $imported_forms[ $old_parent_form_id ] !== (int) $old_parent_form_id ) {
347 // Update all children with this old parent_form_id
348 $new_parent_form_id = (int) $imported_forms[ $old_parent_form_id ];
349 FrmForm::update( $child_form_id, array( 'parent_form_id' => $new_parent_form_id ) );
350 do_action( 'frm_update_child_form_parent_id', $child_form_id, $new_parent_form_id );
351 }
352 }
353 }
354
355 /**
356 * Import all fields for a form
357 *
358 * @since 2.0.13
359 *
360 * TODO: Cut down on params
361 */
362 private static function import_xml_fields( $xml_fields, $form_id, $this_form, &$form_fields, &$imported ) {
363 $in_section = 0;
364 $keys_by_original_field_id = array();
365
366 foreach ( $xml_fields as $field ) {
367 $f = self::fill_field( $field, $form_id );
368
369 self::set_default_value( $f );
370 self::maybe_add_required( $f );
371 self::maybe_update_in_section_variable( $in_section, $f );
372 self::maybe_update_form_select( $f, $imported );
373 self::maybe_update_get_values_form_setting( $imported, $f );
374 self::migrate_placeholders( $f );
375
376 if ( ! empty( $this_form ) ) {
377 // check for field to edit by field id
378 if ( isset( $form_fields[ $f['id'] ] ) ) {
379 FrmField::update( $f['id'], $f );
380 $imported['updated']['fields'] ++;
381
382 unset( $form_fields[ $f['id'] ] );
383
384 //unset old field key
385 if ( isset( $form_fields[ $f['field_key'] ] ) ) {
386 unset( $form_fields[ $f['field_key'] ] );
387 }
388 } elseif ( isset( $form_fields[ $f['field_key'] ] ) ) {
389 $keys_by_original_field_id[ $f['id'] ] = $f['field_key'];
390
391 // check for field to edit by field key
392 unset( $f['id'] );
393
394 FrmField::update( $form_fields[ $f['field_key'] ], $f );
395 $imported['updated']['fields'] ++;
396
397 unset( $form_fields[ $form_fields[ $f['field_key'] ] ] ); //unset old field id
398 unset( $form_fields[ $f['field_key'] ] ); //unset old field key
399 } else {
400 // if no matching field id or key in this form, create the field
401 self::create_imported_field( $f, $imported );
402 }
403 } else {
404
405 self::create_imported_field( $f, $imported );
406 }
407 }
408
409 if ( $keys_by_original_field_id ) {
410 self::maybe_update_field_ids( $form_id, $keys_by_original_field_id );
411 }
412 }
413
414 private static function fill_field( $field, $form_id ) {
415 return array(
416 'id' => (int) $field->id,
417 'field_key' => (string) $field->field_key,
418 'name' => (string) $field->name,
419 'description' => (string) $field->description,
420 'type' => (string) $field->type,
421 'default_value' => FrmAppHelper::maybe_json_decode( (string) $field->default_value ),
422 'field_order' => (int) $field->field_order,
423 'form_id' => (int) $form_id,
424 'required' => (int) $field->required,
425 'options' => FrmAppHelper::maybe_json_decode( (string) $field->options ),
426 'field_options' => FrmAppHelper::maybe_json_decode( (string) $field->field_options ),
427 );
428 }
429
430 /**
431 * @since 4.06
432 */
433 private static function set_default_value( &$f ) {
434 $has_default = array(
435 'text',
436 'email',
437 'url',
438 'textarea',
439 'number',
440 'phone',
441 'date',
442 'hidden',
443 'password',
444 'tag',
445 );
446
447 if ( is_array( $f['default_value'] ) && in_array( $f['type'], $has_default, true ) ) {
448 if ( count( $f['default_value'] ) === 1 ) {
449 $f['default_value'] = '[' . reset( $f['default_value'] ) . ']';
450 } else {
451 $f['default_value'] = reset( $f['default_value'] );
452 }
453 }
454 }
455
456 /**
457 * Make sure the required indicator is set.
458 *
459 * @since 4.05
460 */
461 private static function maybe_add_required( &$f ) {
462 if ( $f['required'] && ! isset( $f['field_options']['required_indicator'] ) ) {
463 $f['field_options']['required_indicator'] = '*';
464 }
465 }
466
467 /**
468 * Update the current in_section value at the beginning of the field loop
469 *
470 * @since 2.0.25
471 * @param int $in_section
472 * @param array $f
473 */
474 private static function maybe_update_in_section_variable( &$in_section, &$f ) {
475 // If we're at the end of a section, switch $in_section is 0
476 if ( in_array( $f['type'], array( 'end_divider', 'break', 'form' ) ) ) {
477 $in_section = 0;
478 }
479
480 // Update the current field's in_section value
481 if ( ! isset( $f['field_options']['in_section'] ) ) {
482 $f['field_options']['in_section'] = $in_section;
483 }
484
485 // If we're starting a new section, switch $in_section to ID of divider
486 if ( $f['type'] == 'divider' ) {
487 $in_section = $f['id'];
488 }
489 }
490
491 /**
492 * Switch the form_select on a repeating field or embedded form if it needs to be switched
493 *
494 * @since 2.0.16
495 *
496 * @param array $f
497 * @param array $imported
498 */
499 private static function maybe_update_form_select( &$f, $imported ) {
500 if ( ! isset( $imported['forms'] ) ) {
501 return;
502 }
503
504 if ( $f['type'] == 'form' || ( $f['type'] == 'divider' && FrmField::is_option_true( $f['field_options'], 'repeat' ) ) ) {
505 if ( FrmField::is_option_true( $f['field_options'], 'form_select' ) ) {
506 $form_select = (int) $f['field_options']['form_select'];
507 if ( isset( $imported['forms'][ $form_select ] ) ) {
508 $f['field_options']['form_select'] = $imported['forms'][ $form_select ];
509 }
510 }
511 }
512 }
513
514 /**
515 * Update the get_values_form setting if the form was imported
516 *
517 * @since 2.01.0
518 *
519 * @param array $imported
520 * @param array $f
521 */
522 private static function maybe_update_get_values_form_setting( $imported, &$f ) {
523 if ( ! isset( $imported['forms'] ) ) {
524 return;
525 }
526
527 if ( FrmField::is_option_true_in_array( $f['field_options'], 'get_values_form' ) ) {
528 $old_form = $f['field_options']['get_values_form'];
529 if ( isset( $imported['forms'][ $old_form ] ) ) {
530 $f['field_options']['get_values_form'] = $imported['forms'][ $old_form ];
531 }
532 }
533 }
534
535 /**
536 * If field settings have been migrated, update the values during import.
537 *
538 * @since 4.0
539 */
540 private static function run_field_migrations( &$f ) {
541 self::migrate_placeholders( $f );
542 $f = apply_filters( 'frm_import_xml_field', $f );
543 }
544
545 /**
546 * @since 4.0
547 */
548 private static function migrate_placeholders( &$f ) {
549 $update_values = self::migrate_field_placeholder( $f, 'clear_on_focus' );
550 foreach ( $update_values as $k => $v ) {
551 $f[ $k ] = $v;
552 }
553
554 $update_values = self::migrate_field_placeholder( $f, 'default_blank' );
555 foreach ( $update_values as $k => $v ) {
556 $f[ $k ] = $v;
557 }
558 }
559
560 /**
561 * Move clear_on_focus or default_blank to placeholder.
562 * Also called during database migration in FrmMigrate.
563 *
564 * @since 4.0
565 * @return array
566 */
567 public static function migrate_field_placeholder( $field, $type ) {
568 $field = (array) $field;
569 $field_options = $field['field_options'];
570 if ( empty( $field_options[ $type ] ) || empty( $field['default_value'] ) ) {
571 return array();
572 }
573
574 $field_options['placeholder'] = is_array( $field['default_value'] ) ? reset( $field['default_value'] ) : $field['default_value'];
575 unset( $field_options['default_blank'], $field_options['clear_on_focus'] );
576
577 $changes = array(
578 'field_options' => $field_options,
579 'default_value' => '',
580 );
581
582 // If a dropdown placeholder was used, remove the option so it won't be included twice.
583 $options = $field['options'];
584 if ( $type === 'default_blank' && is_array( $options ) ) {
585 $default_value = $field['default_value'];
586 if ( is_array( $default_value ) ) {
587 $default_value = reset( $default_value );
588 }
589
590 foreach ( $options as $opt_key => $opt ) {
591 if ( is_array( $opt ) ) {
592 $opt = isset( $opt['value'] ) ? $opt['value'] : ( isset( $opt['label'] ) ? $opt['label'] : reset( $opt ) );
593 }
594
595 if ( $opt == $default_value ) {
596 unset( $options[ $opt_key ] );
597 break;
598 }
599 }
600 $changes['options'] = $options;
601 }
602
603 return $changes;
604 }
605
606 /**
607 * Create an imported field
608 *
609 * @since 2.0.25
610 *
611 * @param array $f
612 * @param array $imported
613 */
614 private static function create_imported_field( $f, &$imported ) {
615 $defaults = self::default_field_options( $f['type'] );
616 $f['field_options'] = array_merge( $defaults, $f['field_options'] );
617
618 $new_id = FrmField::create( $f );
619 if ( $new_id != false ) {
620 $imported['imported']['fields'] ++;
621 do_action( 'frm_after_field_is_imported', $f, $new_id );
622 }
623 }
624
625 /**
626 * Fix field ids for fields that already exist prior to import.
627 *
628 * @since 4.07
629 * @param int $form_id
630 * @param array $keys_by_original_field_id
631 */
632 protected static function maybe_update_field_ids( $form_id, $keys_by_original_field_id ) {
633 global $frm_duplicate_ids;
634
635 $former_duplicate_ids = $frm_duplicate_ids;
636 $where = array(
637 array(
638 'or' => 1,
639 'fi.form_id' => $form_id,
640 'fr.parent_form_id' => $form_id,
641 ),
642 );
643 $fields = FrmField::getAll( $where, 'field_order' );
644 $field_id_by_key = wp_list_pluck( $fields, 'id', 'field_key' );
645
646 foreach ( $fields as $field ) {
647 $before = (array) clone $field;
648 $field = (array) $field;
649 $frm_duplicate_ids = $keys_by_original_field_id;
650 $after = FrmFieldsHelper::switch_field_ids( $field );
651
652 if ( $before['field_options'] !== $after['field_options'] ) {
653 $frm_duplicate_ids = $field_id_by_key;
654 $after = FrmFieldsHelper::switch_field_ids( $after );
655
656 if ( $before['field_options'] !== $after['field_options'] ) {
657 FrmField::update( $field['id'], array( 'field_options' => $after['field_options'] ) );
658 }
659 }
660 }
661
662 $frm_duplicate_ids = $former_duplicate_ids;
663 }
664
665 /**
666 * Updates the custom style setting on import
667 * Convert the post slug to an ID
668 *
669 * @since 2.0.19
670 *
671 * @param array $form
672 */
673 private static function update_custom_style_setting_on_import( &$form ) {
674 if ( ! isset( $form['options']['custom_style'] ) ) {
675 return;
676 }
677
678 if ( is_numeric( $form['options']['custom_style'] ) ) {
679 // Set to default
680 $form['options']['custom_style'] = 1;
681 } else {
682 // Replace the style name with the style ID on import
683 global $wpdb;
684 $table = $wpdb->prefix . 'posts';
685 $where = array(
686 'post_name' => $form['options']['custom_style'],
687 'post_type' => 'frm_styles',
688 );
689 $select = 'ID';
690 $style_id = FrmDb::get_var( $table, $where, $select );
691
692 if ( $style_id ) {
693 $form['options']['custom_style'] = $style_id;
694 } else {
695 // save the old style to maybe update after styles import
696 $form['options']['old_style'] = $form['options']['custom_style'];
697
698 // Set to default
699 $form['options']['custom_style'] = 1;
700 }
701 }
702 }
703
704 /**
705 * After styles are imported, check for any forms that were linked
706 * and link them back up.
707 *
708 * @since 2.2.7
709 */
710 private static function update_custom_style_setting_after_import( $form_id ) {
711 $form = FrmForm::getOne( $form_id );
712
713 if ( $form && isset( $form->options['old_style'] ) ) {
714 $form = (array) $form;
715 $saved_style = $form['options']['custom_style'];
716 $form['options']['custom_style'] = $form['options']['old_style'];
717 self::update_custom_style_setting_on_import( $form );
718 $has_changed = ( $form['options']['custom_style'] != $saved_style && $form['options']['custom_style'] != $form['options']['old_style'] );
719 if ( $has_changed ) {
720 FrmForm::update( $form['id'], $form );
721 }
722 }
723 }
724
725 public static function import_xml_views( $views, $imported ) {
726 $imported['posts'] = array();
727 $form_action_type = FrmFormActionsController::$action_post_type;
728
729 $post_types = array(
730 'frm_display' => 'views',
731 $form_action_type => 'actions',
732 'frm_styles' => 'styles',
733 );
734
735 foreach ( $views as $item ) {
736 $post = array(
737 'post_title' => (string) $item->title,
738 'post_name' => (string) $item->post_name,
739 'post_type' => (string) $item->post_type,
740 'post_password' => (string) $item->post_password,
741 'guid' => (string) $item->guid,
742 'post_status' => (string) $item->status,
743 'post_author' => FrmAppHelper::get_user_id_param( (string) $item->post_author ),
744 'post_id' => (int) $item->post_id,
745 'post_parent' => (int) $item->post_parent,
746 'menu_order' => (int) $item->menu_order,
747 'post_content' => FrmFieldsHelper::switch_field_ids( (string) $item->content ),
748 'post_excerpt' => FrmFieldsHelper::switch_field_ids( (string) $item->excerpt ),
749 'is_sticky' => (string) $item->is_sticky,
750 'comment_status' => (string) $item->comment_status,
751 'post_date' => (string) $item->post_date,
752 'post_date_gmt' => (string) $item->post_date_gmt,
753 'ping_status' => (string) $item->ping_status,
754 'postmeta' => array(),
755 'tax_input' => array(),
756 );
757
758 $old_id = $post['post_id'];
759 self::populate_post( $post, $item, $imported );
760
761 unset( $item );
762
763 $post_id = false;
764 if ( $post['post_type'] == $form_action_type ) {
765 $action_control = FrmFormActionsController::get_form_actions( $post['post_excerpt'] );
766 if ( $action_control && is_object( $action_control ) ) {
767 $post_id = $action_control->maybe_create_action( $post, $imported['form_status'] );
768 }
769 unset( $action_control );
770 } elseif ( $post['post_type'] == 'frm_styles' ) {
771 // Properly encode post content before inserting the post
772 $post['post_content'] = FrmAppHelper::maybe_json_decode( $post['post_content'] );
773 $custom_css = isset( $post['post_content']['custom_css'] ) ? $post['post_content']['custom_css'] : '';
774 $post['post_content'] = FrmAppHelper::prepare_and_encode( $post['post_content'] );
775
776 // Create/update post now
777 $post_id = wp_insert_post( $post );
778 self::maybe_update_custom_css( $custom_css );
779 } else {
780 // Create/update post now
781 $post_id = wp_insert_post( $post );
782 }
783
784 if ( ! is_numeric( $post_id ) ) {
785 continue;
786 }
787
788 self::update_postmeta( $post, $post_id );
789
790 $this_type = 'posts';
791 if ( isset( $post_types[ $post['post_type'] ] ) ) {
792 $this_type = $post_types[ $post['post_type'] ];
793 }
794
795 if ( isset( $post['ID'] ) && $post_id == $post['ID'] ) {
796 $imported['updated'][ $this_type ] ++;
797 } else {
798 $imported['imported'][ $this_type ] ++;
799 }
800
801 $imported['posts'][ (int) $old_id ] = $post_id;
802
803 do_action( 'frm_after_import_view', $post_id, $post );
804
805 unset( $post );
806 }
807
808 self::maybe_update_stylesheet( $imported );
809
810 return $imported;
811 }
812
813 private static function populate_post( &$post, $item, $imported ) {
814 if ( isset( $item->attachment_url ) ) {
815 $post['attachment_url'] = (string) $item->attachment_url;
816 }
817
818 if ( $post['post_type'] == FrmFormActionsController::$action_post_type && isset( $imported['forms'][ (int) $post['menu_order'] ] ) ) {
819 // update to new form id
820 $post['menu_order'] = $imported['forms'][ (int) $post['menu_order'] ];
821 }
822
823 // Don't allow default styles to take over a site's default style
824 if ( 'frm_styles' == $post['post_type'] ) {
825 $post['menu_order'] = 0;
826 }
827
828 foreach ( $item->postmeta as $meta ) {
829 self::populate_postmeta( $post, $meta, $imported );
830 unset( $meta );
831 }
832
833 self::populate_taxonomies( $post, $item );
834
835 self::maybe_editing_post( $post );
836 }
837
838 private static function populate_postmeta( &$post, $meta, $imported ) {
839 global $frm_duplicate_ids;
840
841 $m = array(
842 'key' => (string) $meta->meta_key,
843 'value' => (string) $meta->meta_value,
844 );
845
846 //switch old form and field ids to new ones
847 if ( $m['key'] == 'frm_form_id' && isset( $imported['forms'][ (int) $m['value'] ] ) ) {
848 $m['value'] = $imported['forms'][ (int) $m['value'] ];
849 } else {
850 $m['value'] = FrmAppHelper::maybe_json_decode( $m['value'] );
851
852 if ( ! empty( $frm_duplicate_ids ) ) {
853
854 if ( $m['key'] == 'frm_dyncontent' ) {
855 $m['value'] = FrmFieldsHelper::switch_field_ids( $m['value'] );
856 } elseif ( $m['key'] == 'frm_options' ) {
857
858 foreach ( array( 'date_field_id', 'edate_field_id' ) as $setting_name ) {
859 if ( isset( $m['value'][ $setting_name ] ) && is_numeric( $m['value'][ $setting_name ] ) && isset( $frm_duplicate_ids[ $m['value'][ $setting_name ] ] ) ) {
860 $m['value'][ $setting_name ] = $frm_duplicate_ids[ $m['value'][ $setting_name ] ];
861 }
862 }
863
864 $check_dup_array = array();
865 if ( isset( $m['value']['order_by'] ) && ! empty( $m['value']['order_by'] ) ) {
866 if ( is_numeric( $m['value']['order_by'] ) && isset( $frm_duplicate_ids[ $m['value']['order_by'] ] ) ) {
867 $m['value']['order_by'] = $frm_duplicate_ids[ $m['value']['order_by'] ];
868 } elseif ( is_array( $m['value']['order_by'] ) ) {
869 $check_dup_array[] = 'order_by';
870 }
871 }
872
873 if ( isset( $m['value']['where'] ) && ! empty( $m['value']['where'] ) ) {
874 $check_dup_array[] = 'where';
875 }
876
877 foreach ( $check_dup_array as $check_k ) {
878 foreach ( (array) $m['value'][ $check_k ] as $mk => $mv ) {
879 if ( isset( $frm_duplicate_ids[ $mv ] ) ) {
880 $m['value'][ $check_k ][ $mk ] = $frm_duplicate_ids[ $mv ];
881 }
882 unset( $mk, $mv );
883 }
884 }
885 }
886 }
887 }
888
889 if ( ! is_array( $m['value'] ) ) {
890 $m['value'] = FrmAppHelper::maybe_json_decode( $m['value'] );
891 }
892
893 $post['postmeta'][ (string) $meta->meta_key ] = $m['value'];
894 }
895
896 /**
897 * Add terms to post
898 *
899 * @param array $post by reference
900 * @param object $item The XML object data
901 */
902 private static function populate_taxonomies( &$post, $item ) {
903 foreach ( $item->category as $c ) {
904 $att = $c->attributes();
905 if ( ! isset( $att['nicename'] ) ) {
906 continue;
907 }
908
909 $taxonomy = (string) $att['domain'];
910 if ( is_taxonomy_hierarchical( $taxonomy ) ) {
911 $name = (string) $att['nicename'];
912 $h_term = get_term_by( 'slug', $name, $taxonomy );
913 if ( $h_term ) {
914 $name = $h_term->term_id;
915 }
916 unset( $h_term );
917 } else {
918 $name = (string) $c;
919 }
920
921 if ( ! isset( $post['tax_input'][ $taxonomy ] ) ) {
922 $post['tax_input'][ $taxonomy ] = array();
923 }
924
925 $post['tax_input'][ $taxonomy ][] = $name;
926 unset( $name );
927 }
928 }
929
930 /**
931 * Edit post if the key and created time match
932 */
933 private static function maybe_editing_post( &$post ) {
934 $match_by = array(
935 'post_type' => $post['post_type'],
936 'name' => $post['post_name'],
937 'post_status' => $post['post_status'],
938 'posts_per_page' => 1,
939 );
940
941 if ( in_array( $post['post_status'], array( 'trash', 'draft' ) ) ) {
942 $match_by['include'] = $post['post_id'];
943 unset( $match_by['name'] );
944 }
945
946 $editing = get_posts( $match_by );
947
948 if ( ! empty( $editing ) && current( $editing )->post_date == $post['post_date'] ) {
949 // set the id of the post to edit
950 $post['ID'] = current( $editing )->ID;
951 }
952 }
953
954 private static function update_postmeta( &$post, $post_id ) {
955 foreach ( $post['postmeta'] as $k => $v ) {
956 if ( '_edit_last' == $k ) {
957 $v = FrmAppHelper::get_user_id_param( $v );
958 } elseif ( '_thumbnail_id' == $k && FrmAppHelper::pro_is_installed() ) {
959 // Change the attachment ID.
960 $field_obj = FrmFieldFactory::get_field_type( 'file' );
961 $v = $field_obj->get_file_id( $v );
962 }
963
964 update_post_meta( $post_id, $k, $v );
965
966 unset( $k, $v );
967 }
968 }
969
970 /**
971 * If a template includes custom css, let's include it.
972 * The custom css is included on the default style.
973 *
974 * @since 2.03
975 */
976 private static function maybe_update_custom_css( $custom_css ) {
977 if ( empty( $custom_css ) ) {
978 return;
979 }
980
981 $frm_style = new FrmStyle();
982 $default_style = $frm_style->get_default_style();
983 $default_style->post_content['custom_css'] .= "\r\n\r\n" . $custom_css;
984 $frm_style->save( $default_style );
985 }
986
987 private static function maybe_update_stylesheet( $imported ) {
988 $new_styles = isset( $imported['imported']['styles'] ) && ! empty( $imported['imported']['styles'] );
989 $updated_styles = isset( $imported['updated']['styles'] ) && ! empty( $imported['updated']['styles'] );
990 if ( $new_styles || $updated_styles ) {
991 if ( is_admin() && function_exists( 'get_filesystem_method' ) ) {
992 $frm_style = new FrmStyle();
993 $frm_style->update( 'default' );
994 }
995 foreach ( $imported['forms'] as $form_id ) {
996 self::update_custom_style_setting_after_import( $form_id );
997 }
998 }
999 }
1000
1001 /**
1002 * @param string $message
1003 */
1004 public static function parse_message( $result, &$message, &$errors ) {
1005 if ( is_wp_error( $result ) ) {
1006 $errors[] = $result->get_error_message();
1007 } elseif ( ! $result ) {
1008 return;
1009 }
1010
1011 if ( ! is_array( $result ) ) {
1012 $message = is_string( $result ) ? $result : htmlentities( print_r( $result, 1 ) );
1013
1014 return;
1015 }
1016
1017 $t_strings = array(
1018 'imported' => __( 'Imported', 'formidable' ),
1019 'updated' => __( 'Updated', 'formidable' ),
1020 );
1021
1022 $message = '<ul>';
1023 foreach ( $result as $type => $results ) {
1024 if ( ! isset( $t_strings[ $type ] ) ) {
1025 // only print imported and updated
1026 continue;
1027 }
1028
1029 $s_message = array();
1030 foreach ( $results as $k => $m ) {
1031 self::item_count_message( $m, $k, $s_message );
1032 unset( $k, $m );
1033 }
1034
1035 if ( ! empty( $s_message ) ) {
1036 $message .= '<li><strong>' . $t_strings[ $type ] . ':</strong> ';
1037 $message .= implode( ', ', $s_message );
1038 $message .= '</li>';
1039 }
1040 }
1041
1042 if ( $message == '<ul>' ) {
1043 $message = '';
1044 $errors[] = __( 'Nothing was imported or updated', 'formidable' );
1045 } else {
1046 self::add_form_link_to_message( $result, $message );
1047
1048 $message .= '</ul>';
1049 }
1050 }
1051
1052 public static function item_count_message( $m, $type, &$s_message ) {
1053 if ( ! $m ) {
1054 return;
1055 }
1056
1057 $strings = array(
1058 /* translators: %1$s: Number of items */
1059 'forms' => sprintf( _n( '%1$s Form', '%1$s Forms', $m, 'formidable' ), $m ),
1060 /* translators: %1$s: Number of items */
1061 'fields' => sprintf( _n( '%1$s Field', '%1$s Fields', $m, 'formidable' ), $m ),
1062 /* translators: %1$s: Number of items */
1063 'items' => sprintf( _n( '%1$s Entry', '%1$s Entries', $m, 'formidable' ), $m ),
1064 /* translators: %1$s: Number of items */
1065 'views' => sprintf( _n( '%1$s View', '%1$s Views', $m, 'formidable' ), $m ),
1066 /* translators: %1$s: Number of items */
1067 'posts' => sprintf( _n( '%1$s Post', '%1$s Posts', $m, 'formidable' ), $m ),
1068 /* translators: %1$s: Number of items */
1069 'styles' => sprintf( _n( '%1$s Style', '%1$s Styles', $m, 'formidable' ), $m ),
1070 /* translators: %1$s: Number of items */
1071 'terms' => sprintf( _n( '%1$s Term', '%1$s Terms', $m, 'formidable' ), $m ),
1072 /* translators: %1$s: Number of items */
1073 'actions' => sprintf( _n( '%1$s Form Action', '%1$s Form Actions', $m, 'formidable' ), $m ),
1074 );
1075
1076 $s_message[] = isset( $strings[ $type ] ) ? $strings[ $type ] : ' ' . $m . ' ' . ucfirst( $type );
1077 }
1078
1079 /**
1080 * If a single form was imported, include a link in the success message.
1081 *
1082 * @since 4.0
1083 * @param array $result The response from the XML import.
1084 * @param string $message The response shown on the page after import.
1085 */
1086 private static function add_form_link_to_message( $result, &$message ) {
1087 $total_forms = $result['imported']['forms'] + $result['updated']['forms'];
1088 if ( $total_forms > 1 ) {
1089 return;
1090 }
1091
1092 $primary_form = reset( $result['forms'] );
1093 if ( ! empty( $primary_form ) ) {
1094 $primary_form = FrmForm::getOne( $primary_form );
1095 $form_id = empty( $primary_form->parent_form_id ) ? $primary_form->id : $primary_form->parent_form_id;
1096
1097 $message .= '<li><a href="' . esc_url( FrmForm::get_edit_link( $form_id ) ) . '">' . esc_html__( 'Go to imported form', 'formidable' ) . '</a></li>';
1098 }
1099 }
1100
1101 /**
1102 * Prepare the form options for export
1103 *
1104 * @since 2.0.19
1105 *
1106 * @param string $options
1107 *
1108 * @return string
1109 */
1110 public static function prepare_form_options_for_export( $options ) {
1111 FrmAppHelper::unserialize_or_decode( $options );
1112 // Change custom_style to the post_name instead of ID (1 may be a string)
1113 $not_default = isset( $options['custom_style'] ) && 1 != $options['custom_style'];
1114 if ( $not_default ) {
1115 global $wpdb;
1116 $table = $wpdb->prefix . 'posts';
1117 $where = array( 'ID' => $options['custom_style'] );
1118 $select = 'post_name';
1119
1120 $style_name = FrmDb::get_var( $table, $where, $select );
1121
1122 if ( $style_name ) {
1123 $options['custom_style'] = $style_name;
1124 } else {
1125 $options['custom_style'] = 1;
1126 }
1127 }
1128 self::remove_default_form_options( $options );
1129 $options = serialize( $options );
1130
1131 return self::cdata( $options );
1132 }
1133
1134 /**
1135 * If the saved value is the same as the default, remove it from the export
1136 * This keeps file size down and prevents overriding global settings after import
1137 *
1138 * @since 3.06
1139 */
1140 private static function remove_default_form_options( &$options ) {
1141 $defaults = FrmFormsHelper::get_default_opts();
1142 if ( is_callable( 'FrmProFormsHelper::get_default_opts' ) ) {
1143 $defaults += FrmProFormsHelper::get_default_opts();
1144 }
1145 self::remove_defaults( $defaults, $options );
1146 }
1147
1148 /**
1149 * Remove extra settings from field to keep file size down
1150 *
1151 * @since 3.06
1152 */
1153 public static function prepare_field_for_export( &$field ) {
1154 self::remove_default_field_options( $field );
1155 }
1156
1157 /**
1158 * Remove defaults from field options too
1159 *
1160 * @since 3.06
1161 */
1162 private static function remove_default_field_options( &$field ) {
1163 $defaults = self::default_field_options( $field->type );
1164 if ( empty( $defaults['blank'] ) ) {
1165 $global_settings = new FrmSettings();
1166 $global_defaults = $global_settings->default_options();
1167 $defaults['blank'] = $global_defaults['blank_msg'];
1168 }
1169
1170 $options = $field->field_options;
1171 FrmAppHelper::unserialize_or_decode( $options );
1172 self::remove_defaults( $defaults, $options );
1173 self::remove_default_html( 'custom_html', $defaults, $options );
1174
1175 // Get variations on the defaults.
1176 if ( isset( $options['invalid'] ) ) {
1177 $defaults = array(
1178 /* translators: %s: Field name */
1179 'invalid' => sprintf( __( '%s is invalid', 'formidable' ), $field->name ),
1180 );
1181 self::remove_defaults( $defaults, $options );
1182 }
1183
1184 $field->field_options = serialize( $options );
1185 }
1186
1187 /**
1188 * @since 3.06.03
1189 */
1190 private static function default_field_options( $type ) {
1191 $defaults = FrmFieldsHelper::get_default_field_options( $type );
1192 if ( empty( $defaults['custom_html'] ) ) {
1193 $defaults['custom_html'] = FrmFieldsHelper::get_default_html( $type );
1194 }
1195 return $defaults;
1196 }
1197
1198 /**
1199 * Compare the default array to the saved values and
1200 * remove if they are the same
1201 *
1202 * @since 3.06
1203 */
1204 private static function remove_defaults( $defaults, &$saved ) {
1205 $array_defaults = array_filter( $defaults, 'is_array' );
1206 foreach ( $array_defaults as $d => $default ) {
1207 // compare array defaults
1208 if ( $default == $saved[ $d ] ) {
1209 unset( $saved[ $d ] );
1210 }
1211 unset( $defaults[ $d ] );
1212 }
1213 $saved = array_diff_assoc( (array) $saved, $defaults );
1214 }
1215
1216 /**
1217 * The line endings may prevent html from being equal when it should
1218 *
1219 * @since 3.06
1220 */
1221 private static function remove_default_html( $html_name, $defaults, &$options ) {
1222 if ( ! isset( $options[ $html_name ] ) || ! isset( $defaults[ $html_name ] ) ) {
1223 return;
1224 }
1225
1226 $old_html = str_replace( "\r\n", "\n", $options[ $html_name ] );
1227 $default_html = $defaults[ $html_name ];
1228 if ( $old_html == $default_html ) {
1229 unset( $options[ $html_name ] );
1230
1231 return;
1232 }
1233
1234 // Account for some of the older field default HTML.
1235 $default_html = str_replace( ' id="frm_desc_field_[key]"', '', $default_html );
1236 if ( $old_html == $default_html ) {
1237 unset( $options[ $html_name ] );
1238 }
1239 }
1240
1241 public static function cdata( $str ) {
1242 FrmAppHelper::unserialize_or_decode( $str );
1243 if ( is_array( $str ) ) {
1244 $str = json_encode( $str );
1245 } elseif ( seems_utf8( $str ) == false ) {
1246 $str = utf8_encode( $str );
1247 }
1248
1249 if ( is_numeric( $str ) ) {
1250 return $str;
1251 }
1252
1253 self::remove_invalid_characters_from_xml( $str );
1254
1255 // $str = ent2ncr(esc_html( $str));
1256 $str = '<![CDATA[' . str_replace( ']]>', ']]]]><![CDATA[>', $str ) . ']]>';
1257
1258 return $str;
1259 }
1260
1261 /**
1262 * Remove <US> character (unit separator) from exported strings
1263 *
1264 * @since 2.0.22
1265 *
1266 * @param string $str
1267 */
1268 private static function remove_invalid_characters_from_xml( &$str ) {
1269 // Remove <US> character
1270 $str = str_replace( '\x1F', '', $str );
1271 }
1272
1273 public static function migrate_form_settings_to_actions( $form_options, $form_id, &$imported = array(), $switch = false ) {
1274 // Get post type
1275 $post_type = FrmFormActionsController::$action_post_type;
1276
1277 // Set up imported index, if not set up yet
1278 if ( ! isset( $imported['imported']['actions'] ) ) {
1279 $imported['imported']['actions'] = 0;
1280 }
1281
1282 // Migrate post settings to action
1283 self::migrate_post_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1284
1285 // Migrate email settings to action
1286 self::migrate_email_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1287 }
1288
1289 /**
1290 * Migrate post settings to form action
1291 *
1292 * @param string $post_type
1293 */
1294 private static function migrate_post_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1295 if ( ! isset( $form_options['create_post'] ) || ! $form_options['create_post'] ) {
1296 return;
1297 }
1298
1299 $new_action = array(
1300 'post_type' => $post_type,
1301 'post_excerpt' => 'wppost',
1302 'post_title' => __( 'Create Posts', 'formidable' ),
1303 'menu_order' => $form_id,
1304 'post_status' => 'publish',
1305 'post_content' => array(),
1306 'post_name' => $form_id . '_wppost_1',
1307 );
1308
1309 $post_settings = array(
1310 'post_type',
1311 'post_category',
1312 'post_content',
1313 'post_excerpt',
1314 'post_title',
1315 'post_name',
1316 'post_date',
1317 'post_status',
1318 'post_custom_fields',
1319 'post_password',
1320 'post_parent',
1321 );
1322
1323 foreach ( $post_settings as $post_setting ) {
1324 if ( isset( $form_options[ $post_setting ] ) ) {
1325 $new_action['post_content'][ $post_setting ] = $form_options[ $post_setting ];
1326 }
1327 unset( $post_setting );
1328 }
1329
1330 $new_action['event'] = array( 'create', 'update' );
1331
1332 if ( $switch ) {
1333 // Fields with string or int saved.
1334 $basic_fields = array(
1335 'post_title',
1336 'post_content',
1337 'post_excerpt',
1338 'post_password',
1339 'post_date',
1340 'post_status',
1341 'post_parent',
1342 );
1343
1344 // Fields with arrays saved.
1345 $array_fields = array( 'post_category', 'post_custom_fields' );
1346
1347 $new_action['post_content'] = self::switch_action_field_ids( $new_action['post_content'], $basic_fields, $array_fields );
1348 }
1349 $new_action['post_content'] = json_encode( $new_action['post_content'] );
1350
1351 $exists = get_posts(
1352 array(
1353 'name' => $new_action['post_name'],
1354 'post_type' => $new_action['post_type'],
1355 'post_status' => $new_action['post_status'],
1356 'numberposts' => 1,
1357 )
1358 );
1359
1360 if ( ! $exists ) {
1361 // this isn't an email, but we need to use a class that will always be included
1362 FrmDb::save_json_post( $new_action );
1363 $imported['imported']['actions'] ++;
1364 }
1365 }
1366
1367 /**
1368 * Switch old field IDs for new field IDs in emails and post
1369 *
1370 * @since 2.0
1371 *
1372 * @param array $post_content - check for old field IDs
1373 * @param array $basic_fields - fields with string or int saved
1374 * @param array $array_fields - fields with arrays saved
1375 *
1376 * @return string $post_content - new field IDs
1377 */
1378 private static function switch_action_field_ids( $post_content, $basic_fields, $array_fields = array() ) {
1379 global $frm_duplicate_ids;
1380
1381 // If there aren't IDs that were switched, end now
1382 if ( ! $frm_duplicate_ids ) {
1383 return;
1384 }
1385
1386 // Get old IDs
1387 $old = array_keys( $frm_duplicate_ids );
1388
1389 // Get new IDs
1390 $new = array_values( $frm_duplicate_ids );
1391
1392 // Do a str_replace with each item to set the new IDs
1393 foreach ( $post_content as $key => $setting ) {
1394 if ( ! is_array( $setting ) && in_array( $key, $basic_fields ) ) {
1395 // Replace old IDs with new IDs
1396 $post_content[ $key ] = str_replace( $old, $new, $setting );
1397 } elseif ( is_array( $setting ) && in_array( $key, $array_fields ) ) {
1398 foreach ( $setting as $k => $val ) {
1399 // Replace old IDs with new IDs
1400 $post_content[ $key ][ $k ] = str_replace( $old, $new, $val );
1401 }
1402 }
1403 unset( $key, $setting );
1404 }
1405
1406 return $post_content;
1407 }
1408
1409 private static function migrate_email_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1410 // No old notifications or autoresponders to carry over
1411 if ( ! isset( $form_options['auto_responder'] ) && ! isset( $form_options['notification'] ) && ! isset( $form_options['email_to'] ) ) {
1412 return;
1413 }
1414
1415 // Initialize notifications array
1416 $notifications = array();
1417
1418 // Migrate regular notifications
1419 self::migrate_notifications_to_action( $form_options, $form_id, $notifications );
1420
1421 // Migrate autoresponders
1422 self::migrate_autoresponder_to_action( $form_options, $form_id, $notifications );
1423
1424 if ( empty( $notifications ) ) {
1425 return;
1426 }
1427
1428 foreach ( $notifications as $new_notification ) {
1429 $new_notification['post_type'] = $post_type;
1430 $new_notification['post_excerpt'] = 'email';
1431 $new_notification['post_title'] = __( 'Email Notification', 'formidable' );
1432 $new_notification['menu_order'] = $form_id;
1433 $new_notification['post_status'] = 'publish';
1434
1435 // Switch field IDs and keys, if needed
1436 if ( $switch ) {
1437
1438 // Switch field IDs in email conditional logic
1439 self::switch_email_condition_field_ids( $new_notification['post_content'] );
1440
1441 // Switch all other field IDs in email
1442 $new_notification['post_content'] = FrmFieldsHelper::switch_field_ids( $new_notification['post_content'] );
1443 }
1444 $new_notification['post_content'] = FrmAppHelper::prepare_and_encode( $new_notification['post_content'] );
1445
1446 $exists = get_posts(
1447 array(
1448 'name' => $new_notification['post_name'],
1449 'post_type' => $new_notification['post_type'],
1450 'post_status' => $new_notification['post_status'],
1451 'numberposts' => 1,
1452 )
1453 );
1454
1455 if ( empty( $exists ) ) {
1456 FrmDb::save_json_post( $new_notification );
1457 $imported['imported']['actions'] ++;
1458 }
1459 unset( $new_notification );
1460 }
1461
1462 self::remove_deprecated_notification_settings( $form_id, $form_options );
1463 }
1464
1465 /**
1466 * Remove deprecated notification settings after migration
1467 *
1468 * @since 2.05
1469 *
1470 * @param int|string $form_id
1471 * @param array $form_options
1472 */
1473 private static function remove_deprecated_notification_settings( $form_id, $form_options ) {
1474 $delete_settings = array( 'notification', 'autoresponder', 'email_to' );
1475 foreach ( $delete_settings as $index ) {
1476 if ( isset( $form_options[ $index ] ) ) {
1477 unset( $form_options[ $index ] );
1478 }
1479 }
1480 FrmForm::update( $form_id, array( 'options' => $form_options ) );
1481 }
1482
1483 private static function migrate_notifications_to_action( $form_options, $form_id, &$notifications ) {
1484 if ( ! isset( $form_options['notification'] ) && isset( $form_options['email_to'] ) && ! empty( $form_options['email_to'] ) ) {
1485 // add old settings into notification array
1486 $form_options['notification'] = array( 0 => $form_options );
1487 } elseif ( isset( $form_options['notification']['email_to'] ) ) {
1488 // make sure it's in the correct format
1489 $form_options['notification'] = array( 0 => $form_options['notification'] );
1490 }
1491
1492 if ( isset( $form_options['notification'] ) && is_array( $form_options['notification'] ) ) {
1493 foreach ( $form_options['notification'] as $email_key => $notification ) {
1494
1495 $atts = array(
1496 'email_to' => '',
1497 'reply_to' => '',
1498 'reply_to_name' => '',
1499 'event' => '',
1500 'form_id' => $form_id,
1501 'email_key' => $email_key,
1502 );
1503
1504 // Format the email data
1505 self::format_email_data( $atts, $notification );
1506
1507 if ( isset( $notification['twilio'] ) && $notification['twilio'] ) {
1508 do_action( 'frm_create_twilio_action', $atts, $notification );
1509 }
1510
1511 // Setup the new notification
1512 $new_notification = array();
1513 self::setup_new_notification( $new_notification, $notification, $atts );
1514
1515 $notifications[] = $new_notification;
1516 }
1517 }
1518 }
1519
1520 private static function format_email_data( &$atts, $notification ) {
1521 // Format email_to
1522 self::format_email_to_data( $atts, $notification );
1523
1524 // Format the reply to email and name
1525 $reply_fields = array(
1526 'reply_to' => '',
1527 'reply_to_name' => '',
1528 );
1529 foreach ( $reply_fields as $f => $val ) {
1530 if ( isset( $notification[ $f ] ) ) {
1531 $atts[ $f ] = $notification[ $f ];
1532 if ( 'custom' == $notification[ $f ] ) {
1533 $atts[ $f ] = $notification[ 'cust_' . $f ];
1534 } elseif ( is_numeric( $atts[ $f ] ) && ! empty( $atts[ $f ] ) ) {
1535 $atts[ $f ] = '[' . $atts[ $f ] . ']';
1536 }
1537 }
1538 unset( $f, $val );
1539 }
1540
1541 // Format event
1542 $atts['event'] = array( 'create' );
1543 if ( isset( $notification['update_email'] ) && 1 == $notification['update_email'] ) {
1544 $atts['event'][] = 'update';
1545 } elseif ( isset( $notification['update_email'] ) && 2 == $notification['update_email'] ) {
1546 $atts['event'] = array( 'update' );
1547 }
1548 }
1549
1550 private static function format_email_to_data( &$atts, $notification ) {
1551 if ( isset( $notification['email_to'] ) ) {
1552 $atts['email_to'] = preg_split( '/ (,|;) /', $notification['email_to'] );
1553 } else {
1554 $atts['email_to'] = array();
1555 }
1556
1557 if ( isset( $notification['also_email_to'] ) ) {
1558 $email_fields = (array) $notification['also_email_to'];
1559 $atts['email_to'] = array_merge( $email_fields, $atts['email_to'] );
1560 unset( $email_fields );
1561 }
1562
1563 foreach ( $atts['email_to'] as $key => $email_field ) {
1564
1565 if ( is_numeric( $email_field ) ) {
1566 $atts['email_to'][ $key ] = '[' . $email_field . ']';
1567 }
1568
1569 if ( strpos( $email_field, '|' ) ) {
1570 $email_opt = explode( '|', $email_field );
1571 if ( isset( $email_opt[0] ) ) {
1572 $atts['email_to'][ $key ] = '[' . $email_opt[0] . ' show=' . $email_opt[1] . ']';
1573 }
1574 unset( $email_opt );
1575 }
1576 }
1577 $atts['email_to'] = implode( ', ', $atts['email_to'] );
1578 }
1579
1580 private static function setup_new_notification( &$new_notification, $notification, $atts ) {
1581 // Set up new notification
1582 $new_notification = array(
1583 'post_content' => array(
1584 'email_to' => $atts['email_to'],
1585 'event' => $atts['event'],
1586 ),
1587 'post_name' => $atts['form_id'] . '_email_' . $atts['email_key'],
1588 );
1589
1590 // Add more fields to the new notification
1591 $add_fields = array( 'email_message', 'email_subject', 'plain_text', 'inc_user_info', 'conditions' );
1592 foreach ( $add_fields as $add_field ) {
1593 if ( isset( $notification[ $add_field ] ) ) {
1594 $new_notification['post_content'][ $add_field ] = $notification[ $add_field ];
1595 } elseif ( in_array( $add_field, array( 'plain_text', 'inc_user_info' ) ) ) {
1596 $new_notification['post_content'][ $add_field ] = 0;
1597 } else {
1598 $new_notification['post_content'][ $add_field ] = '';
1599 }
1600 unset( $add_field );
1601 }
1602
1603 // Set reply to
1604 $new_notification['post_content']['reply_to'] = $atts['reply_to'];
1605
1606 // Set from
1607 if ( ! empty( $atts['reply_to'] ) || ! empty( $atts['reply_to_name'] ) ) {
1608 $new_notification['post_content']['from'] = ( empty( $atts['reply_to_name'] ) ? '[sitename]' : $atts['reply_to_name'] ) . ' <' . ( empty( $atts['reply_to'] ) ? '[admin_email]' : $atts['reply_to'] ) . '>';
1609 }
1610 }
1611
1612 /**
1613 * Switch field IDs in pre-2.0 email conditional logic
1614 *
1615 * @param $post_content array, pass by reference
1616 */
1617 private static function switch_email_condition_field_ids( &$post_content ) {
1618 // Switch field IDs in conditional logic
1619 if ( isset( $post_content['conditions'] ) && is_array( $post_content['conditions'] ) ) {
1620 foreach ( $post_content['conditions'] as $email_key => $val ) {
1621 if ( is_numeric( $email_key ) ) {
1622 $post_content['conditions'][ $email_key ] = self::switch_action_field_ids( $val, array( 'hide_field' ) );
1623 }
1624 unset( $email_key, $val );
1625 }
1626 }
1627 }
1628
1629 private static function migrate_autoresponder_to_action( $form_options, $form_id, &$notifications ) {
1630 if ( isset( $form_options['auto_responder'] ) && $form_options['auto_responder'] && isset( $form_options['ar_email_message'] ) && $form_options['ar_email_message'] ) {
1631 // migrate autoresponder
1632
1633 $email_field = isset( $form_options['ar_email_to'] ) ? $form_options['ar_email_to'] : 0;
1634 if ( strpos( $email_field, '|' ) ) {
1635 // data from entries field
1636 $email_field = explode( '|', $email_field );
1637 if ( isset( $email_field[1] ) ) {
1638 $email_field = $email_field[1];
1639 }
1640 }
1641 if ( is_numeric( $email_field ) && ! empty( $email_field ) ) {
1642 $email_field = '[' . $email_field . ']';
1643 }
1644
1645 $notification = $form_options;
1646 $new_notification2 = array(
1647 'post_content' => array(
1648 'email_message' => $notification['ar_email_message'],
1649 'email_subject' => isset( $notification['ar_email_subject'] ) ? $notification['ar_email_subject'] : '',
1650 'email_to' => $email_field,
1651 'plain_text' => isset( $notification['ar_plain_text'] ) ? $notification['ar_plain_text'] : 0,
1652 'inc_user_info' => 0,
1653 ),
1654 'post_name' => $form_id . '_email_' . count( $notifications ),
1655 );
1656
1657 $reply_to = isset( $notification['ar_reply_to'] ) ? $notification['ar_reply_to'] : '';
1658 $reply_to_name = isset( $notification['ar_reply_to_name'] ) ? $notification['ar_reply_to_name'] : '';
1659
1660 if ( ! empty( $reply_to ) ) {
1661 $new_notification2['post_content']['reply_to'] = $reply_to;
1662 }
1663
1664 if ( ! empty( $reply_to ) || ! empty( $reply_to_name ) ) {
1665 $new_notification2['post_content']['from'] = ( empty( $reply_to_name ) ? '[sitename]' : $reply_to_name ) . ' <' . ( empty( $reply_to ) ? '[admin_email]' : $reply_to ) . '>';
1666 }
1667
1668 $notifications[] = $new_notification2;
1669 unset( $new_notification2 );
1670 }
1671 }
1672
1673 /**
1674 * PHP 8 backward compatibility for the libxml_disable_entity_loader function
1675 *
1676 * @param boolean $disable
1677 *
1678 * @return boolean
1679 */
1680 public static function maybe_libxml_disable_entity_loader( $loader ) {
1681 if ( version_compare( phpversion(), '8.0', '<' ) && function_exists( 'libxml_disable_entity_loader' ) ) {
1682 $loader = libxml_disable_entity_loader( $loader ); // phpcs:disable Generic.PHP.DeprecatedFunctions.Deprecated
1683 }
1684
1685 return $loader;
1686 }
1687
1688 public static function check_if_libxml_disable_entity_loader_exists() {
1689 return version_compare( phpversion(), '8.0', '<' ) && ! function_exists( 'libxml_disable_entity_loader' );
1690 }
1691 }
1692
1693