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

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