Completed
Push — master ( 745295...508f1a )
by Stephanie
04:10 queued 01:47
created

FrmXMLHelper::migrate_field_placeholder()   C

Complexity

Conditions 12
Paths 47

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
nc 47
nop 2
dl 0
loc 38
rs 6.9666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
		$imported = self::pre_import_data();
63
64
		foreach ( array( 'term', 'form', 'view' ) as $item_type ) {
65
			// Grab cats, tags, and terms, or forms or posts.
66
			if ( isset( $xml->{$item_type} ) ) {
67
				$function_name = 'import_xml_' . $item_type . 's';
68
				$imported      = self::$function_name( $xml->{$item_type}, $imported );
69
				unset( $function_name, $xml->{$item_type} );
70
			}
71
		}
72
73
		return apply_filters( 'frm_importing_xml', $imported, $xml );
74
	}
75
76
	/**
77
	 * @since 3.06
78
	 * @return array
79
	 */
80
	private static function pre_import_data() {
81
		$defaults = array(
82
			'forms'   => 0,
83
			'fields'  => 0,
84
			'terms'   => 0,
85
			'posts'   => 0,
86
			'views'   => 0,
87
			'actions' => 0,
88
			'styles'  => 0,
89
		);
90
91
		return array(
92
			'imported' => $defaults,
93
			'updated'  => $defaults,
94
			'forms'    => array(),
95
			'terms'    => array(),
96
		);
97
	}
98
99
	public static function import_xml_terms( $terms, $imported ) {
100
		foreach ( $terms as $t ) {
101
			if ( term_exists( (string) $t->term_slug, (string) $t->term_taxonomy ) ) {
102
				continue;
103
			}
104
105
			$parent = self::get_term_parent_id( $t );
106
107
			$term = wp_insert_term(
108
				(string) $t->term_name,
109
				(string) $t->term_taxonomy,
110
				array(
111
					'slug'        => (string) $t->term_slug,
112
					'description' => (string) $t->term_description,
113
					'parent'      => empty( $parent ) ? 0 : $parent,
114
				)
115
			);
116
117
			if ( $term && is_array( $term ) ) {
118
				$imported['imported']['terms'] ++;
119
				$imported['terms'][ (int) $t->term_id ] = $term['term_id'];
120
			}
121
122
			unset( $term, $t );
123
		}
124
125
		return $imported;
126
	}
127
128
	/**
129
	 * @since 2.0.8
130
	 */
131
	private static function get_term_parent_id( $t ) {
132
		$parent = (string) $t->term_parent;
133
		if ( ! empty( $parent ) ) {
134
			$parent = term_exists( (string) $t->term_parent, (string) $t->term_taxonomy );
135
			if ( $parent ) {
136
				$parent = $parent['term_id'];
137
			} else {
138
				$parent = 0;
139
			}
140
		}
141
142
		return $parent;
143
	}
144
145
	public static function import_xml_forms( $forms, $imported ) {
146
		$child_forms = array();
147
148
		// Import child forms first
149
		self::put_child_forms_first( $forms );
150
151
		foreach ( $forms as $item ) {
152
			$form = self::fill_form( $item );
153
154
			self::update_custom_style_setting_on_import( $form );
155
156
			$this_form = self::maybe_get_form( $form );
157
158
			$old_id      = false;
159
			$form_fields = false;
160
			if ( ! empty( $this_form ) ) {
161
				$form_id = $this_form->id;
162
				$old_id  = $this_form->id;
163
				self::update_form( $this_form, $form, $imported );
164
165
				$form_fields = self::get_form_fields( $form_id );
166
			} else {
167
				$form_id = FrmForm::create( $form );
168
				if ( $form_id ) {
169
					if ( empty( $form['parent_form_id'] ) ) {
170
						// Don't include the repeater form in the imported count.
171
						$imported['imported']['forms'] ++;
172
					}
173
174
					// Keep track of whether this specific form was updated or not.
175
					$imported['form_status'][ $form_id ] = 'imported';
176
					self::track_imported_child_forms( (int) $form_id, $form['parent_form_id'], $child_forms );
177
				}
178
			}
179
180
			self::import_xml_fields( $item->field, $form_id, $this_form, $form_fields, $imported );
181
182
			self::delete_removed_fields( $form_fields );
183
184
			// Update field ids/keys to new ones.
185
			do_action( 'frm_after_duplicate_form', $form_id, $form, array( 'old_id' => $old_id ) );
186
187
			$imported['forms'][ (int) $item->id ] = $form_id;
188
189
			// Send pre 2.0 form options through function that creates actions.
190
			self::migrate_form_settings_to_actions( $form['options'], $form_id, $imported, true );
191
192
			do_action( 'frm_after_import_form', $form_id, $form );
193
194
			unset( $form, $item );
195
		}
196
197
		self::maybe_update_child_form_parent_id( $imported['forms'], $child_forms );
198
199
		return $imported;
200
	}
201
202
	private static function fill_form( $item ) {
203
		$form = array(
204
			'id'             => (int) $item->id,
205
			'form_key'       => (string) $item->form_key,
206
			'name'           => (string) $item->name,
207
			'description'    => (string) $item->description,
208
			'options'        => (string) $item->options,
209
			'logged_in'      => (int) $item->logged_in,
210
			'is_template'    => (int) $item->is_template,
211
			'editable'       => (int) $item->editable,
212
			'status'         => (string) $item->status,
213
			'parent_form_id' => isset( $item->parent_form_id ) ? (int) $item->parent_form_id : 0,
214
			'created_at'     => gmdate( 'Y-m-d H:i:s', strtotime( (string) $item->created_at ) ),
215
		);
216
217
		if ( empty( $item->created_at ) ) {
218
			$form['created_at'] = current_time( 'mysql', 1 );
219
		}
220
221
		$form['options'] = FrmAppHelper::maybe_json_decode( $form['options'] );
222
223
		return $form;
224
	}
225
226
	private static function maybe_get_form( $form ) {
227
		// if template, allow to edit if form keys match, otherwise, creation date must also match
228
		$edit_query = array(
229
			'form_key'    => $form['form_key'],
230
			'is_template' => $form['is_template'],
231
		);
232
		if ( ! $form['is_template'] ) {
233
			$edit_query['created_at'] = $form['created_at'];
234
		}
235
236
		$edit_query = apply_filters( 'frm_match_xml_form', $edit_query, $form );
237
238
		return FrmForm::getAll( $edit_query, '', 1 );
239
	}
240
241
	private static function update_form( $this_form, $form, &$imported ) {
242
		$form_id = $this_form->id;
243
		FrmForm::update( $form_id, $form );
244
		if ( empty( $form['parent_form_id'] ) ) {
245
			// Don't include the repeater form in the updated count.
246
			$imported['updated']['forms'] ++;
247
		}
248
249
		// Keep track of whether this specific form was updated or not
250
		$imported['form_status'][ $form_id ] = 'updated';
251
	}
252
253
	private static function get_form_fields( $form_id ) {
254
		$form_fields = FrmField::get_all_for_form( $form_id, '', 'exclude', 'exclude' );
255
		$old_fields  = array();
256
		foreach ( $form_fields as $f ) {
257
			$old_fields[ $f->id ]        = $f;
258
			$old_fields[ $f->field_key ] = $f->id;
259
			unset( $f );
260
		}
261
		$form_fields = $old_fields;
262
263
		return $form_fields;
264
	}
265
266
	/**
267
	 * Delete any fields attached to this form that were not included in the template
268
	 */
269
	private static function delete_removed_fields( $form_fields ) {
270
		if ( ! empty( $form_fields ) ) {
271
			foreach ( $form_fields as $field ) {
272
				if ( is_object( $field ) ) {
273
					FrmField::destroy( $field->id );
274
				}
275
				unset( $field );
276
			}
277
		}
278
	}
279
280
	/**
281
	 * Put child forms first so they will be imported before parents
282
	 *
283
	 * @since 2.0.16
284
	 *
285
	 * @param array $forms
286
	 */
287
	private static function put_child_forms_first( &$forms ) {
288
		$child_forms   = array();
289
		$regular_forms = array();
290
291
		foreach ( $forms as $form ) {
292
			$parent_form_id = isset( $form->parent_form_id ) ? (int) $form->parent_form_id : 0;
293
294
			if ( $parent_form_id ) {
295
				$child_forms[] = $form;
296
			} else {
297
				$regular_forms[] = $form;
298
			}
299
		}
300
301
		$forms = array_merge( $child_forms, $regular_forms );
302
	}
303
304
	/**
305
	 * Keep track of all imported child forms
306
	 *
307
	 * @since 2.0.16
308
	 *
309
	 * @param int $form_id
310
	 * @param int $parent_form_id
311
	 * @param array $child_forms
312
	 */
313
	private static function track_imported_child_forms( $form_id, $parent_form_id, &$child_forms ) {
314
		if ( $parent_form_id ) {
315
			$child_forms[ $form_id ] = $parent_form_id;
316
		}
317
	}
318
319
	/**
320
	 * Update the parent_form_id on imported child forms
321
	 * Child forms are imported first so their parent_form_id will need to be updated after the parent is imported
322
	 *
323
	 * @since 2.0.6
324
	 *
325
	 * @param array $imported_forms
326
	 * @param array $child_forms
327
	 */
328
	private static function maybe_update_child_form_parent_id( $imported_forms, $child_forms ) {
329
		foreach ( $child_forms as $child_form_id => $old_parent_form_id ) {
330
331
			if ( isset( $imported_forms[ $old_parent_form_id ] ) && $imported_forms[ $old_parent_form_id ] != $old_parent_form_id ) {
332
				// Update all children with this old parent_form_id
333
				$new_parent_form_id = (int) $imported_forms[ $old_parent_form_id ];
334
335
				FrmForm::update( $child_form_id, array( 'parent_form_id' => $new_parent_form_id ) );
336
			}
337
		}
338
	}
339
340
	/**
341
	 * Import all fields for a form
342
	 *
343
	 * @since 2.0.13
344
	 *
345
	 * TODO: Cut down on params
346
	 */
347
	private static function import_xml_fields( $xml_fields, $form_id, $this_form, &$form_fields, &$imported ) {
348
		$in_section = 0;
349
350
		foreach ( $xml_fields as $field ) {
351
			$f = self::fill_field( $field, $form_id );
352
353
			$has_default = array(
354
				'text',
355
				'email',
356
				'url',
357
				'textarea',
358
				'number',
359
				'phone',
360
				'date',
361
				'hidden',
362
				'password',
363
				'tag',
364
			);
365
			if ( is_array( $f['default_value'] ) && in_array( $f['type'], $has_default, true ) ) {
366
				if ( count( $f['default_value'] ) === 1 ) {
367
					$f['default_value'] = '[' . reset( $f['default_value'] ) . ']';
368
				} else {
369
					$f['default_value'] = reset( $f['default_value'] );
370
				}
371
			}
372
373
			self::maybe_add_required( $f );
374
			self::maybe_update_in_section_variable( $in_section, $f );
375
			self::maybe_update_form_select( $f, $imported );
376
			self::maybe_update_get_values_form_setting( $imported, $f );
377
			self::migrate_placeholders( $f );
378
379
			if ( ! empty( $this_form ) ) {
380
				// check for field to edit by field id
381
				if ( isset( $form_fields[ $f['id'] ] ) ) {
382
					FrmField::update( $f['id'], $f );
383
					$imported['updated']['fields'] ++;
384
385
					unset( $form_fields[ $f['id'] ] );
386
387
					//unset old field key
388
					if ( isset( $form_fields[ $f['field_key'] ] ) ) {
389
						unset( $form_fields[ $f['field_key'] ] );
390
					}
391
				} elseif ( isset( $form_fields[ $f['field_key'] ] ) ) {
392
					// check for field to edit by field key
393
					unset( $f['id'] );
394
395
					FrmField::update( $form_fields[ $f['field_key'] ], $f );
396
					$imported['updated']['fields'] ++;
397
398
					unset( $form_fields[ $form_fields[ $f['field_key'] ] ] ); //unset old field id
399
					unset( $form_fields[ $f['field_key'] ] ); //unset old field key
400
				} else {
401
					// if no matching field id or key in this form, create the field
402
					self::create_imported_field( $f, $imported );
403
				}
404
			} else {
405
406
				self::create_imported_field( $f, $imported );
407
			}
408
		}
409
	}
410
411
	private static function fill_field( $field, $form_id ) {
412
		return array(
413
			'id'            => (int) $field->id,
414
			'field_key'     => (string) $field->field_key,
415
			'name'          => (string) $field->name,
416
			'description'   => (string) $field->description,
417
			'type'          => (string) $field->type,
418
			'default_value' => FrmAppHelper::maybe_json_decode( (string) $field->default_value ),
419
			'field_order'   => (int) $field->field_order,
420
			'form_id'       => (int) $form_id,
421
			'required'      => (int) $field->required,
422
			'options'       => FrmAppHelper::maybe_json_decode( (string) $field->options ),
423
			'field_options' => FrmAppHelper::maybe_json_decode( (string) $field->field_options ),
424
		);
425
	}
426
427
	/**
428
	 * Make sure the required indicator is set.
429
	 *
430
	 * @since 4.05
431
	 */
432
	private static function maybe_add_required( &$f ) {
433
		if ( $f['required'] && ! isset( $f['field_options']['required_indicator'] ) ) {
434
			$f['field_options']['required_indicator'] = '*';
435
		}
436
	}
437
438
	/**
439
	 * Update the current in_section value
440
	 *
441
	 * @since 2.0.25
442
	 *
443
	 * @param int $in_section
444
	 * @param array $f
445
	 */
446
	private static function maybe_update_in_section_variable( &$in_section, &$f ) {
447
		// If we're at the end of a section, switch $in_section is 0
448
		if ( in_array( $f['type'], array( 'end_divider', 'break', 'form' ) ) ) {
449
			$in_section = 0;
450
		}
451
452
		// Update the current field's in_section value
453
		if ( ! isset( $f['field_options']['in_section'] ) ) {
454
			$f['field_options']['in_section'] = $in_section;
455
		}
456
457
		// If we're starting a new section, switch $in_section to ID of divider
458
		if ( $f['type'] == 'divider' ) {
459
			$in_section = $f['id'];
460
		}
461
	}
462
463
	/**
464
	 * Switch the form_select on a repeating field or embedded form if it needs to be switched
465
	 *
466
	 * @since 2.0.16
467
	 *
468
	 * @param array $f
469
	 * @param array $imported
470
	 */
471
	private static function maybe_update_form_select( &$f, $imported ) {
472
		if ( ! isset( $imported['forms'] ) ) {
473
			return;
474
		}
475
476
		if ( $f['type'] == 'form' || ( $f['type'] == 'divider' && FrmField::is_option_true( $f['field_options'], 'repeat' ) ) ) {
477 View Code Duplication
			if ( FrmField::is_option_true( $f['field_options'], 'form_select' ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
478
				$form_select = (int) $f['field_options']['form_select'];
479
				if ( isset( $imported['forms'][ $form_select ] ) ) {
480
					$f['field_options']['form_select'] = $imported['forms'][ $form_select ];
481
				}
482
			}
483
		}
484
	}
485
486
	/**
487
	 * Update the get_values_form setting if the form was imported
488
	 *
489
	 * @since 2.01.0
490
	 *
491
	 * @param array $imported
492
	 * @param array $f
493
	 */
494
	private static function maybe_update_get_values_form_setting( $imported, &$f ) {
495
		if ( ! isset( $imported['forms'] ) ) {
496
			return;
497
		}
498
499 View Code Duplication
		if ( FrmField::is_option_true_in_array( $f['field_options'], 'get_values_form' ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
500
			$old_form = $f['field_options']['get_values_form'];
501
			if ( isset( $imported['forms'][ $old_form ] ) ) {
502
				$f['field_options']['get_values_form'] = $imported['forms'][ $old_form ];
503
			}
504
		}
505
	}
506
507
	/**
508
	 * If field settings have been migrated, update the values during import.
509
	 *
510
	 * @since 4.0
511
	 */
512
	private static function run_field_migrations( &$f ) {
513
		self::migrate_placeholders( $f );
514
		$f = apply_filters( 'frm_import_xml_field', $f );
515
	}
516
517
	/**
518
	 * @since 4.0
519
	 */
520
	private static function migrate_placeholders( &$f ) {
521
		$update_values = self::migrate_field_placeholder( $f, 'clear_on_focus' );
522
		foreach ( $update_values as $k => $v ) {
523
			$f[ $k ] = $v;
524
		}
525
526
		$update_values = self::migrate_field_placeholder( $f, 'default_blank' );
527
		foreach ( $update_values as $k => $v ) {
528
			$f[ $k ] = $v;
529
		}
530
	}
531
532
	/**
533
	 * Move clear_on_focus or default_blank to placeholder.
534
	 * Also called during database migration in FrmMigrate.
535
	 *
536
	 * @since 4.0
537
	 * @return array
538
	 */
539
	public static function migrate_field_placeholder( $field, $type ) {
540
		$field = (array) $field;
541
		$field_options = $field['field_options'];
542
		if ( empty( $field_options[ $type ] ) || empty( $field['default_value'] ) ) {
543
			return array();
544
		}
545
546
		$field_options['placeholder'] = is_array( $field['default_value'] ) ? reset( $field['default_value'] ) : $field['default_value'];
547
		unset( $field_options['default_blank'], $field_options['clear_on_focus'] );
548
549
		$changes = array(
550
			'field_options' => $field_options,
551
			'default_value' => '',
552
		);
553
554
		// If a dropdown placeholder was used, remove the option so it won't be included twice.
555
		$options = $field['options'];
556
		if ( $type === 'default_blank' && is_array( $options ) ) {
557
			$default_value = $field['default_value'];
558
			if ( is_array( $default_value ) ) {
559
				$default_value = reset( $default_value );
560
			}
561
562
			foreach ( $options as $opt_key => $opt ) {
563
				if ( is_array( $opt ) ) {
564
					$opt = isset( $opt['value'] ) ? $opt['value'] : ( isset( $opt['label'] ) ? $opt['label'] : reset( $opt ) );
565
				}
566
567
				if ( $opt == $default_value ) {
568
					unset( $options[ $opt_key ] );
569
					break;
570
				}
571
			}
572
			$changes['options'] = $options;
573
		}
574
575
		return $changes;
576
	}
577
578
	/**
579
	 * Create an imported field
580
	 *
581
	 * @since 2.0.25
582
	 *
583
	 * @param array $f
584
	 * @param array $imported
585
	 */
586
	private static function create_imported_field( $f, &$imported ) {
587
		$defaults           = self::default_field_options( $f['type'] );
588
		$f['field_options'] = array_merge( $defaults, $f['field_options'] );
589
590
		$new_id = FrmField::create( $f );
591
		if ( $new_id != false ) {
592
			$imported['imported']['fields'] ++;
593
			do_action( 'frm_after_field_is_imported', $f, $new_id );
594
		}
595
	}
596
597
	/**
598
	 * Updates the custom style setting on import
599
	 * Convert the post slug to an ID
600
	 *
601
	 * @since 2.0.19
602
	 *
603
	 * @param array $form
604
	 */
605
	private static function update_custom_style_setting_on_import( &$form ) {
606
		if ( ! isset( $form['options']['custom_style'] ) ) {
607
			return;
608
		}
609
610
		if ( is_numeric( $form['options']['custom_style'] ) ) {
611
			// Set to default
612
			$form['options']['custom_style'] = 1;
613
		} else {
614
			// Replace the style name with the style ID on import
615
			global $wpdb;
616
			$table    = $wpdb->prefix . 'posts';
617
			$where    = array(
618
				'post_name' => $form['options']['custom_style'],
619
				'post_type' => 'frm_styles',
620
			);
621
			$select   = 'ID';
622
			$style_id = FrmDb::get_var( $table, $where, $select );
623
624
			if ( $style_id ) {
625
				$form['options']['custom_style'] = $style_id;
626
			} else {
627
				// save the old style to maybe update after styles import
628
				$form['options']['old_style'] = $form['options']['custom_style'];
629
630
				// Set to default
631
				$form['options']['custom_style'] = 1;
632
			}
633
		}
634
	}
635
636
	/**
637
	 * After styles are imported, check for any forms that were linked
638
	 * and link them back up.
639
	 *
640
	 * @since 2.2.7
641
	 */
642
	private static function update_custom_style_setting_after_import( $form_id ) {
643
		$form = FrmForm::getOne( $form_id );
644
645
		if ( $form && isset( $form->options['old_style'] ) ) {
646
			$form                            = (array) $form;
647
			$saved_style                     = $form['options']['custom_style'];
648
			$form['options']['custom_style'] = $form['options']['old_style'];
649
			self::update_custom_style_setting_on_import( $form );
650
			$has_changed = ( $form['options']['custom_style'] != $saved_style && $form['options']['custom_style'] != $form['options']['old_style'] );
651
			if ( $has_changed ) {
652
				FrmForm::update( $form['id'], $form );
653
			}
654
		}
655
	}
656
657
	public static function import_xml_views( $views, $imported ) {
658
		$imported['posts'] = array();
659
		$form_action_type  = FrmFormActionsController::$action_post_type;
660
661
		$post_types = array(
662
			'frm_display'     => 'views',
663
			$form_action_type => 'actions',
664
			'frm_styles'      => 'styles',
665
		);
666
667
		foreach ( $views as $item ) {
668
			$post = array(
669
				'post_title'     => (string) $item->title,
670
				'post_name'      => (string) $item->post_name,
671
				'post_type'      => (string) $item->post_type,
672
				'post_password'  => (string) $item->post_password,
673
				'guid'           => (string) $item->guid,
674
				'post_status'    => (string) $item->status,
675
				'post_author'    => FrmAppHelper::get_user_id_param( (string) $item->post_author ),
676
				'post_id'        => (int) $item->post_id,
677
				'post_parent'    => (int) $item->post_parent,
678
				'menu_order'     => (int) $item->menu_order,
679
				'post_content'   => FrmFieldsHelper::switch_field_ids( (string) $item->content ),
680
				'post_excerpt'   => FrmFieldsHelper::switch_field_ids( (string) $item->excerpt ),
681
				'is_sticky'      => (string) $item->is_sticky,
682
				'comment_status' => (string) $item->comment_status,
683
				'post_date'      => (string) $item->post_date,
684
				'post_date_gmt'  => (string) $item->post_date_gmt,
685
				'ping_status'    => (string) $item->ping_status,
686
				'postmeta'       => array(),
687
				'tax_input'      => array(),
688
			);
689
690
			$old_id = $post['post_id'];
691
			self::populate_post( $post, $item, $imported );
692
693
			unset( $item );
694
695
			$post_id = false;
696
			if ( $post['post_type'] == $form_action_type ) {
697
				$action_control = FrmFormActionsController::get_form_actions( $post['post_excerpt'] );
698
				if ( $action_control && is_object( $action_control ) ) {
699
					$post_id = $action_control->maybe_create_action( $post, $imported['form_status'] );
700
				}
701
				unset( $action_control );
702
			} elseif ( $post['post_type'] == 'frm_styles' ) {
703
				// Properly encode post content before inserting the post
704
				$post['post_content'] = FrmAppHelper::maybe_json_decode( $post['post_content'] );
705
				$custom_css           = isset( $post['post_content']['custom_css'] ) ? $post['post_content']['custom_css'] : '';
706
				$post['post_content'] = FrmAppHelper::prepare_and_encode( $post['post_content'] );
707
708
				// Create/update post now
709
				$post_id = wp_insert_post( $post );
710
				self::maybe_update_custom_css( $custom_css );
711
			} else {
712
				// Create/update post now
713
				$post_id = wp_insert_post( $post );
714
			}
715
716
			if ( ! is_numeric( $post_id ) ) {
717
				continue;
718
			}
719
720
			self::update_postmeta( $post, $post_id );
721
722
			$this_type = 'posts';
723
			if ( isset( $post_types[ $post['post_type'] ] ) ) {
724
				$this_type = $post_types[ $post['post_type'] ];
725
			}
726
727
			if ( isset( $post['ID'] ) && $post_id == $post['ID'] ) {
728
				$imported['updated'][ $this_type ] ++;
729
			} else {
730
				$imported['imported'][ $this_type ] ++;
731
			}
732
733
			$imported['posts'][ (int) $old_id ] = $post_id;
734
735
			do_action( 'frm_after_import_view', $post_id, $post );
736
737
			unset( $post );
738
		}
739
740
		self::maybe_update_stylesheet( $imported );
741
742
		return $imported;
743
	}
744
745
	private static function populate_post( &$post, $item, $imported ) {
746
		if ( isset( $item->attachment_url ) ) {
747
			$post['attachment_url'] = (string) $item->attachment_url;
748
		}
749
750
		if ( $post['post_type'] == FrmFormActionsController::$action_post_type && isset( $imported['forms'][ (int) $post['menu_order'] ] ) ) {
751
			// update to new form id
752
			$post['menu_order'] = $imported['forms'][ (int) $post['menu_order'] ];
753
		}
754
755
		// Don't allow default styles to take over a site's default style
756
		if ( 'frm_styles' == $post['post_type'] ) {
757
			$post['menu_order'] = 0;
758
		}
759
760
		foreach ( $item->postmeta as $meta ) {
761
			self::populate_postmeta( $post, $meta, $imported );
762
			unset( $meta );
763
		}
764
765
		self::populate_taxonomies( $post, $item );
766
767
		self::maybe_editing_post( $post );
768
	}
769
770
	private static function populate_postmeta( &$post, $meta, $imported ) {
771
		global $frm_duplicate_ids;
772
773
		$m = array(
774
			'key'   => (string) $meta->meta_key,
775
			'value' => (string) $meta->meta_value,
776
		);
777
778
		//switch old form and field ids to new ones
779
		if ( $m['key'] == 'frm_form_id' && isset( $imported['forms'][ (int) $m['value'] ] ) ) {
780
			$m['value'] = $imported['forms'][ (int) $m['value'] ];
781
		} else {
782
			$m['value'] = FrmAppHelper::maybe_json_decode( $m['value'] );
783
784
			if ( ! empty( $frm_duplicate_ids ) ) {
785
786
				if ( $m['key'] == 'frm_dyncontent' ) {
787
					$m['value'] = FrmFieldsHelper::switch_field_ids( $m['value'] );
788
				} elseif ( $m['key'] == 'frm_options' ) {
789
790
					foreach ( array( 'date_field_id', 'edate_field_id' ) as $setting_name ) {
791
						if ( isset( $m['value'][ $setting_name ] ) && is_numeric( $m['value'][ $setting_name ] ) && isset( $frm_duplicate_ids[ $m['value'][ $setting_name ] ] ) ) {
792
							$m['value'][ $setting_name ] = $frm_duplicate_ids[ $m['value'][ $setting_name ] ];
793
						}
794
					}
795
796
					$check_dup_array = array();
797
					if ( isset( $m['value']['order_by'] ) && ! empty( $m['value']['order_by'] ) ) {
798
						if ( is_numeric( $m['value']['order_by'] ) && isset( $frm_duplicate_ids[ $m['value']['order_by'] ] ) ) {
799
							$m['value']['order_by'] = $frm_duplicate_ids[ $m['value']['order_by'] ];
800
						} elseif ( is_array( $m['value']['order_by'] ) ) {
801
							$check_dup_array[] = 'order_by';
802
						}
803
					}
804
805
					if ( isset( $m['value']['where'] ) && ! empty( $m['value']['where'] ) ) {
806
						$check_dup_array[] = 'where';
807
					}
808
809
					foreach ( $check_dup_array as $check_k ) {
810
						foreach ( (array) $m['value'][ $check_k ] as $mk => $mv ) {
811
							if ( isset( $frm_duplicate_ids[ $mv ] ) ) {
812
								$m['value'][ $check_k ][ $mk ] = $frm_duplicate_ids[ $mv ];
813
							}
814
							unset( $mk, $mv );
815
						}
816
					}
817
				}
818
			}
819
		}
820
821
		if ( ! is_array( $m['value'] ) ) {
822
			$m['value'] = FrmAppHelper::maybe_json_decode( $m['value'] );
823
		}
824
825
		$post['postmeta'][ (string) $meta->meta_key ] = $m['value'];
826
	}
827
828
	/**
829
	 * Add terms to post
830
	 *
831
	 * @param array $post by reference
832
	 * @param object $item The XML object data
833
	 */
834
	private static function populate_taxonomies( &$post, $item ) {
835
		foreach ( $item->category as $c ) {
836
			$att = $c->attributes();
837
			if ( ! isset( $att['nicename'] ) ) {
838
				continue;
839
			}
840
841
			$taxonomy = (string) $att['domain'];
842
			if ( is_taxonomy_hierarchical( $taxonomy ) ) {
843
				$name   = (string) $att['nicename'];
844
				$h_term = get_term_by( 'slug', $name, $taxonomy );
845
				if ( $h_term ) {
846
					$name = $h_term->term_id;
847
				}
848
				unset( $h_term );
849
			} else {
850
				$name = (string) $c;
851
			}
852
853
			if ( ! isset( $post['tax_input'][ $taxonomy ] ) ) {
854
				$post['tax_input'][ $taxonomy ] = array();
855
			}
856
857
			$post['tax_input'][ $taxonomy ][] = $name;
858
			unset( $name );
859
		}
860
	}
861
862
	/**
863
	 * Edit post if the key and created time match
864
	 */
865
	private static function maybe_editing_post( &$post ) {
866
		$match_by = array(
867
			'post_type'      => $post['post_type'],
868
			'name'           => $post['post_name'],
869
			'post_status'    => $post['post_status'],
870
			'posts_per_page' => 1,
871
		);
872
873
		if ( in_array( $post['post_status'], array( 'trash', 'draft' ) ) ) {
874
			$match_by['include'] = $post['post_id'];
875
			unset( $match_by['name'] );
876
		}
877
878
		$editing = get_posts( $match_by );
879
880
		if ( ! empty( $editing ) && current( $editing )->post_date == $post['post_date'] ) {
881
			// set the id of the post to edit
882
			$post['ID'] = current( $editing )->ID;
883
		}
884
	}
885
886
	private static function update_postmeta( &$post, $post_id ) {
887
		foreach ( $post['postmeta'] as $k => $v ) {
888
			if ( '_edit_last' == $k ) {
889
				$v = FrmAppHelper::get_user_id_param( $v );
890
			} elseif ( '_thumbnail_id' == $k && FrmAppHelper::pro_is_installed() ) {
891
				// Change the attachment ID.
892
				$field_obj = FrmFieldFactory::get_field_type( 'file' );
893
				$v         = $field_obj->get_file_id( $v );
894
			}
895
896
			update_post_meta( $post_id, $k, $v );
897
898
			unset( $k, $v );
899
		}
900
	}
901
902
	/**
903
	 * If a template includes custom css, let's include it.
904
	 * The custom css is included on the default style.
905
	 *
906
	 * @since 2.03
907
	 */
908
	private static function maybe_update_custom_css( $custom_css ) {
909
		if ( empty( $custom_css ) ) {
910
			return;
911
		}
912
913
		$frm_style                                 = new FrmStyle();
914
		$default_style                             = $frm_style->get_default_style();
915
		$default_style->post_content['custom_css'] .= "\r\n\r\n" . $custom_css;
916
		$frm_style->save( $default_style );
917
	}
918
919
	private static function maybe_update_stylesheet( $imported ) {
920
		$new_styles     = isset( $imported['imported']['styles'] ) && ! empty( $imported['imported']['styles'] );
921
		$updated_styles = isset( $imported['updated']['styles'] ) && ! empty( $imported['updated']['styles'] );
922
		if ( $new_styles || $updated_styles ) {
923
			if ( is_admin() && function_exists( 'get_filesystem_method' ) ) {
924
				$frm_style = new FrmStyle();
925
				$frm_style->update( 'default' );
926
			}
927
			foreach ( $imported['forms'] as $form_id ) {
928
				self::update_custom_style_setting_after_import( $form_id );
929
			}
930
		}
931
	}
932
933
	/**
934
	 * @param string $message
935
	 */
936
	public static function parse_message( $result, &$message, &$errors ) {
937
		if ( is_wp_error( $result ) ) {
938
			$errors[] = $result->get_error_message();
939
		} elseif ( ! $result ) {
940
			return;
941
		}
942
943
		if ( ! is_array( $result ) ) {
944
			$message = is_string( $result ) ? $result : htmlentities( print_r( $result, 1 ) );
945
946
			return;
947
		}
948
949
		$t_strings = array(
950
			'imported' => __( 'Imported', 'formidable' ),
951
			'updated'  => __( 'Updated', 'formidable' ),
952
		);
953
954
		$message = '<ul>';
955
		foreach ( $result as $type => $results ) {
956
			if ( ! isset( $t_strings[ $type ] ) ) {
957
				// only print imported and updated
958
				continue;
959
			}
960
961
			$s_message = array();
962
			foreach ( $results as $k => $m ) {
963
				self::item_count_message( $m, $k, $s_message );
964
				unset( $k, $m );
965
			}
966
967
			if ( ! empty( $s_message ) ) {
968
				$message .= '<li><strong>' . $t_strings[ $type ] . ':</strong> ';
969
				$message .= implode( ', ', $s_message );
970
				$message .= '</li>';
971
			}
972
		}
973
974
		if ( $message == '<ul>' ) {
975
			$message  = '';
976
			$errors[] = __( 'Nothing was imported or updated', 'formidable' );
977
		} else {
978
			self::add_form_link_to_message( $result, $message );
979
980
			$message .= '</ul>';
981
		}
982
	}
983
984
	public static function item_count_message( $m, $type, &$s_message ) {
985
		if ( ! $m ) {
986
			return;
987
		}
988
989
		$strings = array(
990
			/* translators: %1$s: Number of items */
991
			'forms'   => sprintf( _n( '%1$s Form', '%1$s Forms', $m, 'formidable' ), $m ),
992
			/* translators: %1$s: Number of items */
993
			'fields'  => sprintf( _n( '%1$s Field', '%1$s Fields', $m, 'formidable' ), $m ),
994
			/* translators: %1$s: Number of items */
995
			'items'   => sprintf( _n( '%1$s Entry', '%1$s Entries', $m, 'formidable' ), $m ),
996
			/* translators: %1$s: Number of items */
997
			'views'   => sprintf( _n( '%1$s View', '%1$s Views', $m, 'formidable' ), $m ),
998
			/* translators: %1$s: Number of items */
999
			'posts'   => sprintf( _n( '%1$s Post', '%1$s Posts', $m, 'formidable' ), $m ),
1000
			/* translators: %1$s: Number of items */
1001
			'styles'  => sprintf( _n( '%1$s Style', '%1$s Styles', $m, 'formidable' ), $m ),
1002
			/* translators: %1$s: Number of items */
1003
			'terms'   => sprintf( _n( '%1$s Term', '%1$s Terms', $m, 'formidable' ), $m ),
1004
			/* translators: %1$s: Number of items */
1005
			'actions' => sprintf( _n( '%1$s Form Action', '%1$s Form Actions', $m, 'formidable' ), $m ),
1006
		);
1007
1008
		$s_message[] = isset( $strings[ $type ] ) ? $strings[ $type ] : ' ' . $m . ' ' . ucfirst( $type );
1009
	}
1010
1011
	/**
1012
	 * If a single form was imported, include a link in the success message.
1013
	 *
1014
	 * @since 4.0
1015
	 * @param array  $result The response from the XML import.
1016
	 * @param string $message The response shown on the page after import.
1017
	 */
1018
	private static function add_form_link_to_message( $result, &$message ) {
1019
		$total_forms = $result['imported']['forms'] + $result['updated']['forms'];
1020
		if ( $total_forms > 1 ) {
1021
			return;
1022
		}
1023
1024
		$primary_form = reset( $result['forms'] );
1025
		if ( ! empty( $primary_form ) ) {
1026
			$primary_form = FrmForm::getOne( $primary_form );
1027
			$form_id      = empty( $primary_form->parent_form_id ) ? $primary_form->id : $primary_form->parent_form_id;
1028
1029
			$message .= '<li><a href="' . esc_url( FrmForm::get_edit_link( $form_id ) ) . '">' . esc_html__( 'Go to imported form', 'formidable' ) . '</a></li>';
1030
		}
1031
	}
1032
1033
	/**
1034
	 * Prepare the form options for export
1035
	 *
1036
	 * @since 2.0.19
1037
	 *
1038
	 * @param string $options
1039
	 *
1040
	 * @return string
1041
	 */
1042
	public static function prepare_form_options_for_export( $options ) {
1043
		FrmAppHelper::unserialize_or_decode( $options );
1044
		// Change custom_style to the post_name instead of ID (1 may be a string)
1045
		$not_default = isset( $options['custom_style'] ) && 1 != $options['custom_style'];
1046
		if ( $not_default ) {
1047
			global $wpdb;
1048
			$table  = $wpdb->prefix . 'posts';
1049
			$where  = array( 'ID' => $options['custom_style'] );
1050
			$select = 'post_name';
1051
1052
			$style_name = FrmDb::get_var( $table, $where, $select );
1053
1054
			if ( $style_name ) {
1055
				$options['custom_style'] = $style_name;
1056
			} else {
1057
				$options['custom_style'] = 1;
1058
			}
1059
		}
1060
		self::remove_default_form_options( $options );
1061
		$options = serialize( $options );
1062
1063
		return self::cdata( $options );
1064
	}
1065
1066
	/**
1067
	 * If the saved value is the same as the default, remove it from the export
1068
	 * This keeps file size down and prevents overriding global settings after import
1069
	 *
1070
	 * @since 3.06
1071
	 */
1072
	private static function remove_default_form_options( &$options ) {
1073
		$defaults = FrmFormsHelper::get_default_opts();
1074
		if ( is_callable( 'FrmProFormsHelper::get_default_opts' ) ) {
1075
			$defaults += FrmProFormsHelper::get_default_opts();
1076
		}
1077
		self::remove_defaults( $defaults, $options );
1078
	}
1079
1080
	/**
1081
	 * Remove extra settings from field to keep file size down
1082
	 *
1083
	 * @since 3.06
1084
	 */
1085
	public static function prepare_field_for_export( &$field ) {
1086
		self::remove_default_field_options( $field );
1087
	}
1088
1089
	/**
1090
	 * Remove defaults from field options too
1091
	 *
1092
	 * @since 3.06
1093
	 */
1094
	private static function remove_default_field_options( &$field ) {
1095
		$defaults = self::default_field_options( $field->type );
1096
		if ( empty( $defaults['blank'] ) ) {
1097
			$global_settings   = new FrmSettings();
1098
			$global_defaults   = $global_settings->default_options();
1099
			$defaults['blank'] = $global_defaults['blank_msg'];
1100
		}
1101
1102
		$options = $field->field_options;
1103
		FrmAppHelper::unserialize_or_decode( $options );
1104
		self::remove_defaults( $defaults, $options );
1105
		self::remove_default_html( 'custom_html', $defaults, $options );
1106
1107
		// Get variations on the defaults.
1108
		if ( isset( $options['invalid'] ) ) {
1109
			$defaults = array(
1110
				/* translators: %s: Field name */
1111
				'invalid' => sprintf( __( '%s is invalid', 'formidable' ), $field->name ),
1112
			);
1113
			self::remove_defaults( $defaults, $options );
1114
		}
1115
1116
		$field->field_options = serialize( $options );
1117
	}
1118
1119
	/**
1120
	 * @since 3.06.03
1121
	 */
1122
	private static function default_field_options( $type ) {
1123
		$defaults = FrmFieldsHelper::get_default_field_options( $type );
1124
		if ( empty( $defaults['custom_html'] ) ) {
1125
			$defaults['custom_html'] = FrmFieldsHelper::get_default_html( $type );
1126
		}
1127
		return $defaults;
1128
	}
1129
1130
	/**
1131
	 * Compare the default array to the saved values and
1132
	 * remove if they are the same
1133
	 *
1134
	 * @since 3.06
1135
	 */
1136
	private static function remove_defaults( $defaults, &$saved ) {
1137
		$array_defaults = array_filter( $defaults, 'is_array' );
1138
		foreach ( $array_defaults as $d => $default ) {
1139
			// compare array defaults
1140
			if ( $default == $saved[ $d ] ) {
1141
				unset( $saved[ $d ] );
1142
			}
1143
			unset( $defaults[ $d ] );
1144
		}
1145
		$saved = array_diff_assoc( (array) $saved, $defaults );
1146
	}
1147
1148
	/**
1149
	 * The line endings may prevent html from being equal when it should
1150
	 *
1151
	 * @since 3.06
1152
	 */
1153
	private static function remove_default_html( $html_name, $defaults, &$options ) {
1154
		if ( ! isset( $options[ $html_name ] ) || ! isset( $defaults[ $html_name ] ) ) {
1155
			return;
1156
		}
1157
1158
		$old_html     = str_replace( "\r\n", "\n", $options[ $html_name ] );
1159
		$default_html = $defaults[ $html_name ];
1160
		if ( $old_html == $default_html ) {
1161
			unset( $options[ $html_name ] );
1162
1163
			return;
1164
		}
1165
1166
		// Account for some of the older field default HTML.
1167
		$default_html = str_replace( ' id="frm_desc_field_[key]"', '', $default_html );
1168
		if ( $old_html == $default_html ) {
1169
			unset( $options[ $html_name ] );
1170
		}
1171
	}
1172
1173
	public static function cdata( $str ) {
1174
		FrmAppHelper::unserialize_or_decode( $str );
1175
		if ( is_array( $str ) ) {
1176
			$str = json_encode( $str );
1177
		} elseif ( seems_utf8( $str ) == false ) {
1178
			$str = utf8_encode( $str );
1179
		}
1180
1181
		if ( is_numeric( $str ) ) {
1182
			return $str;
1183
		}
1184
1185
		self::remove_invalid_characters_from_xml( $str );
1186
1187
		// $str = ent2ncr(esc_html( $str));
1188
		$str = '<![CDATA[' . str_replace( ']]>', ']]]]><![CDATA[>', $str ) . ']]>';
1189
1190
		return $str;
1191
	}
1192
1193
	/**
1194
	 * Remove <US> character (unit separator) from exported strings
1195
	 *
1196
	 * @since 2.0.22
1197
	 *
1198
	 * @param string $str
1199
	 */
1200
	private static function remove_invalid_characters_from_xml( &$str ) {
1201
		// Remove <US> character
1202
		$str = str_replace( '\x1F', '', $str );
1203
	}
1204
1205
	public static function migrate_form_settings_to_actions( $form_options, $form_id, &$imported = array(), $switch = false ) {
1206
		// Get post type
1207
		$post_type = FrmFormActionsController::$action_post_type;
1208
1209
		// Set up imported index, if not set up yet
1210
		if ( ! isset( $imported['imported']['actions'] ) ) {
1211
			$imported['imported']['actions'] = 0;
1212
		}
1213
1214
		// Migrate post settings to action
1215
		self::migrate_post_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1216
1217
		// Migrate email settings to action
1218
		self::migrate_email_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
1219
	}
1220
1221
	/**
1222
	 * Migrate post settings to form action
1223
	 *
1224
	 * @param string $post_type
1225
	 */
1226
	private static function migrate_post_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1227
		if ( ! isset( $form_options['create_post'] ) || ! $form_options['create_post'] ) {
1228
			return;
1229
		}
1230
1231
		$new_action = array(
1232
			'post_type'    => $post_type,
1233
			'post_excerpt' => 'wppost',
1234
			'post_title'   => __( 'Create Posts', 'formidable' ),
1235
			'menu_order'   => $form_id,
1236
			'post_status'  => 'publish',
1237
			'post_content' => array(),
1238
			'post_name'    => $form_id . '_wppost_1',
1239
		);
1240
1241
		$post_settings = array(
1242
			'post_type',
1243
			'post_category',
1244
			'post_content',
1245
			'post_excerpt',
1246
			'post_title',
1247
			'post_name',
1248
			'post_date',
1249
			'post_status',
1250
			'post_custom_fields',
1251
			'post_password',
1252
		);
1253
1254
		foreach ( $post_settings as $post_setting ) {
1255
			if ( isset( $form_options[ $post_setting ] ) ) {
1256
				$new_action['post_content'][ $post_setting ] = $form_options[ $post_setting ];
1257
			}
1258
			unset( $post_setting );
1259
		}
1260
1261
		$new_action['event'] = array( 'create', 'update' );
1262
1263
		if ( $switch ) {
1264
			// Fields with string or int saved.
1265
			$basic_fields = array(
1266
				'post_title',
1267
				'post_content',
1268
				'post_excerpt',
1269
				'post_password',
1270
				'post_date',
1271
				'post_status',
1272
			);
1273
1274
			// Fields with arrays saved.
1275
			$array_fields = array( 'post_category', 'post_custom_fields' );
1276
1277
			$new_action['post_content'] = self::switch_action_field_ids( $new_action['post_content'], $basic_fields, $array_fields );
1278
		}
1279
		$new_action['post_content'] = json_encode( $new_action['post_content'] );
1280
1281
		$exists = get_posts(
1282
			array(
1283
				'name'        => $new_action['post_name'],
1284
				'post_type'   => $new_action['post_type'],
1285
				'post_status' => $new_action['post_status'],
1286
				'numberposts' => 1,
1287
			)
1288
		);
1289
1290
		if ( ! $exists ) {
1291
			// this isn't an email, but we need to use a class that will always be included
1292
			FrmDb::save_json_post( $new_action );
1293
			$imported['imported']['actions'] ++;
1294
		}
1295
	}
1296
1297
	/**
1298
	 * Switch old field IDs for new field IDs in emails and post
1299
	 *
1300
	 * @since 2.0
1301
	 *
1302
	 * @param array $post_content - check for old field IDs
1303
	 * @param array $basic_fields - fields with string or int saved
1304
	 * @param array $array_fields - fields with arrays saved
1305
	 *
1306
	 * @return string $post_content - new field IDs
1307
	 */
1308
	private static function switch_action_field_ids( $post_content, $basic_fields, $array_fields = array() ) {
1309
		global $frm_duplicate_ids;
1310
1311
		// If there aren't IDs that were switched, end now
1312
		if ( ! $frm_duplicate_ids ) {
1313
			return;
1314
		}
1315
1316
		// Get old IDs
1317
		$old = array_keys( $frm_duplicate_ids );
1318
1319
		// Get new IDs
1320
		$new = array_values( $frm_duplicate_ids );
1321
1322
		// Do a str_replace with each item to set the new IDs
1323
		foreach ( $post_content as $key => $setting ) {
1324
			if ( ! is_array( $setting ) && in_array( $key, $basic_fields ) ) {
1325
				// Replace old IDs with new IDs
1326
				$post_content[ $key ] = str_replace( $old, $new, $setting );
1327
			} elseif ( is_array( $setting ) && in_array( $key, $array_fields ) ) {
1328
				foreach ( $setting as $k => $val ) {
1329
					// Replace old IDs with new IDs
1330
					$post_content[ $key ][ $k ] = str_replace( $old, $new, $val );
1331
				}
1332
			}
1333
			unset( $key, $setting );
1334
		}
1335
1336
		return $post_content;
1337
	}
1338
1339
	private static function migrate_email_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1340
		// No old notifications or autoresponders to carry over
1341
		if ( ! isset( $form_options['auto_responder'] ) && ! isset( $form_options['notification'] ) && ! isset( $form_options['email_to'] ) ) {
1342
			return;
1343
		}
1344
1345
		// Initialize notifications array
1346
		$notifications = array();
1347
1348
		// Migrate regular notifications
1349
		self::migrate_notifications_to_action( $form_options, $form_id, $notifications );
1350
1351
		// Migrate autoresponders
1352
		self::migrate_autoresponder_to_action( $form_options, $form_id, $notifications );
1353
1354
		if ( empty( $notifications ) ) {
1355
			return;
1356
		}
1357
1358
		foreach ( $notifications as $new_notification ) {
1359
			$new_notification['post_type']    = $post_type;
1360
			$new_notification['post_excerpt'] = 'email';
1361
			$new_notification['post_title']   = __( 'Email Notification', 'formidable' );
1362
			$new_notification['menu_order']   = $form_id;
1363
			$new_notification['post_status']  = 'publish';
1364
1365
			// Switch field IDs and keys, if needed
1366
			if ( $switch ) {
1367
1368
				// Switch field IDs in email conditional logic
1369
				self::switch_email_condition_field_ids( $new_notification['post_content'] );
1370
1371
				// Switch all other field IDs in email
1372
				$new_notification['post_content'] = FrmFieldsHelper::switch_field_ids( $new_notification['post_content'] );
1373
			}
1374
			$new_notification['post_content'] = FrmAppHelper::prepare_and_encode( $new_notification['post_content'] );
1375
1376
			$exists = get_posts(
1377
				array(
1378
					'name'        => $new_notification['post_name'],
1379
					'post_type'   => $new_notification['post_type'],
1380
					'post_status' => $new_notification['post_status'],
1381
					'numberposts' => 1,
1382
				)
1383
			);
1384
1385
			if ( empty( $exists ) ) {
1386
				FrmDb::save_json_post( $new_notification );
1387
				$imported['imported']['actions'] ++;
1388
			}
1389
			unset( $new_notification );
1390
		}
1391
1392
		self::remove_deprecated_notification_settings( $form_id, $form_options );
1393
	}
1394
1395
	/**
1396
	 * Remove deprecated notification settings after migration
1397
	 *
1398
	 * @since 2.05
1399
	 *
1400
	 * @param int|string $form_id
1401
	 * @param array $form_options
1402
	 */
1403
	private static function remove_deprecated_notification_settings( $form_id, $form_options ) {
1404
		$delete_settings = array( 'notification', 'autoresponder', 'email_to' );
1405
		foreach ( $delete_settings as $index ) {
1406
			if ( isset( $form_options[ $index ] ) ) {
1407
				unset( $form_options[ $index ] );
1408
			}
1409
		}
1410
		FrmForm::update( $form_id, array( 'options' => $form_options ) );
1411
	}
1412
1413
	private static function migrate_notifications_to_action( $form_options, $form_id, &$notifications ) {
1414
		if ( ! isset( $form_options['notification'] ) && isset( $form_options['email_to'] ) && ! empty( $form_options['email_to'] ) ) {
1415
			// add old settings into notification array
1416
			$form_options['notification'] = array( 0 => $form_options );
1417
		} elseif ( isset( $form_options['notification']['email_to'] ) ) {
1418
			// make sure it's in the correct format
1419
			$form_options['notification'] = array( 0 => $form_options['notification'] );
1420
		}
1421
1422
		if ( isset( $form_options['notification'] ) && is_array( $form_options['notification'] ) ) {
1423
			foreach ( $form_options['notification'] as $email_key => $notification ) {
1424
1425
				$atts = array(
1426
					'email_to'      => '',
1427
					'reply_to'      => '',
1428
					'reply_to_name' => '',
1429
					'event'         => '',
1430
					'form_id'       => $form_id,
1431
					'email_key'     => $email_key,
1432
				);
1433
1434
				// Format the email data
1435
				self::format_email_data( $atts, $notification );
1436
1437
				if ( isset( $notification['twilio'] ) && $notification['twilio'] ) {
1438
					do_action( 'frm_create_twilio_action', $atts, $notification );
1439
				}
1440
1441
				// Setup the new notification
1442
				$new_notification = array();
1443
				self::setup_new_notification( $new_notification, $notification, $atts );
1444
1445
				$notifications[] = $new_notification;
1446
			}
1447
		}
1448
	}
1449
1450
	private static function format_email_data( &$atts, $notification ) {
1451
		// Format email_to
1452
		self::format_email_to_data( $atts, $notification );
1453
1454
		// Format the reply to email and name
1455
		$reply_fields = array(
1456
			'reply_to'      => '',
1457
			'reply_to_name' => '',
1458
		);
1459
		foreach ( $reply_fields as $f => $val ) {
1460
			if ( isset( $notification[ $f ] ) ) {
1461
				$atts[ $f ] = $notification[ $f ];
1462
				if ( 'custom' == $notification[ $f ] ) {
1463
					$atts[ $f ] = $notification[ 'cust_' . $f ];
1464
				} elseif ( is_numeric( $atts[ $f ] ) && ! empty( $atts[ $f ] ) ) {
1465
					$atts[ $f ] = '[' . $atts[ $f ] . ']';
1466
				}
1467
			}
1468
			unset( $f, $val );
1469
		}
1470
1471
		// Format event
1472
		$atts['event'] = array( 'create' );
1473
		if ( isset( $notification['update_email'] ) && 1 == $notification['update_email'] ) {
1474
			$atts['event'][] = 'update';
1475
		} elseif ( isset( $notification['update_email'] ) && 2 == $notification['update_email'] ) {
1476
			$atts['event'] = array( 'update' );
1477
		}
1478
	}
1479
1480
	private static function format_email_to_data( &$atts, $notification ) {
1481
		if ( isset( $notification['email_to'] ) ) {
1482
			$atts['email_to'] = preg_split( '/ (,|;) /', $notification['email_to'] );
1483
		} else {
1484
			$atts['email_to'] = array();
1485
		}
1486
1487
		if ( isset( $notification['also_email_to'] ) ) {
1488
			$email_fields     = (array) $notification['also_email_to'];
1489
			$atts['email_to'] = array_merge( $email_fields, $atts['email_to'] );
1490
			unset( $email_fields );
1491
		}
1492
1493
		foreach ( $atts['email_to'] as $key => $email_field ) {
1494
1495
			if ( is_numeric( $email_field ) ) {
1496
				$atts['email_to'][ $key ] = '[' . $email_field . ']';
1497
			}
1498
1499
			if ( strpos( $email_field, '|' ) ) {
1500
				$email_opt = explode( '|', $email_field );
1501
				if ( isset( $email_opt[0] ) ) {
1502
					$atts['email_to'][ $key ] = '[' . $email_opt[0] . ' show=' . $email_opt[1] . ']';
1503
				}
1504
				unset( $email_opt );
1505
			}
1506
		}
1507
		$atts['email_to'] = implode( ', ', $atts['email_to'] );
1508
	}
1509
1510
	private static function setup_new_notification( &$new_notification, $notification, $atts ) {
1511
		// Set up new notification
1512
		$new_notification = array(
1513
			'post_content' => array(
1514
				'email_to' => $atts['email_to'],
1515
				'event'    => $atts['event'],
1516
			),
1517
			'post_name'    => $atts['form_id'] . '_email_' . $atts['email_key'],
1518
		);
1519
1520
		// Add more fields to the new notification
1521
		$add_fields = array( 'email_message', 'email_subject', 'plain_text', 'inc_user_info', 'conditions' );
1522
		foreach ( $add_fields as $add_field ) {
1523
			if ( isset( $notification[ $add_field ] ) ) {
1524
				$new_notification['post_content'][ $add_field ] = $notification[ $add_field ];
1525
			} elseif ( in_array( $add_field, array( 'plain_text', 'inc_user_info' ) ) ) {
1526
				$new_notification['post_content'][ $add_field ] = 0;
1527
			} else {
1528
				$new_notification['post_content'][ $add_field ] = '';
1529
			}
1530
			unset( $add_field );
1531
		}
1532
1533
		// Set reply to
1534
		$new_notification['post_content']['reply_to'] = $atts['reply_to'];
1535
1536
		// Set from
1537
		if ( ! empty( $atts['reply_to'] ) || ! empty( $atts['reply_to_name'] ) ) {
1538
			$new_notification['post_content']['from'] = ( empty( $atts['reply_to_name'] ) ? '[sitename]' : $atts['reply_to_name'] ) . ' <' . ( empty( $atts['reply_to'] ) ? '[admin_email]' : $atts['reply_to'] ) . '>';
1539
		}
1540
	}
1541
1542
	/**
1543
	 * Switch field IDs in pre-2.0 email conditional logic
1544
	 *
1545
	 * @param $post_content array, pass by reference
1546
	 */
1547
	private static function switch_email_condition_field_ids( &$post_content ) {
1548
		// Switch field IDs in conditional logic
1549
		if ( isset( $post_content['conditions'] ) && is_array( $post_content['conditions'] ) ) {
1550
			foreach ( $post_content['conditions'] as $email_key => $val ) {
1551
				if ( is_numeric( $email_key ) ) {
1552
					$post_content['conditions'][ $email_key ] = self::switch_action_field_ids( $val, array( 'hide_field' ) );
1553
				}
1554
				unset( $email_key, $val );
1555
			}
1556
		}
1557
	}
1558
1559
	private static function migrate_autoresponder_to_action( $form_options, $form_id, &$notifications ) {
1560
		if ( isset( $form_options['auto_responder'] ) && $form_options['auto_responder'] && isset( $form_options['ar_email_message'] ) && $form_options['ar_email_message'] ) {
1561
			// migrate autoresponder
1562
1563
			$email_field = isset( $form_options['ar_email_to'] ) ? $form_options['ar_email_to'] : 0;
1564
			if ( strpos( $email_field, '|' ) ) {
1565
				// data from entries field
1566
				$email_field = explode( '|', $email_field );
1567
				if ( isset( $email_field[1] ) ) {
1568
					$email_field = $email_field[1];
1569
				}
1570
			}
1571
			if ( is_numeric( $email_field ) && ! empty( $email_field ) ) {
1572
				$email_field = '[' . $email_field . ']';
1573
			}
1574
1575
			$notification      = $form_options;
1576
			$new_notification2 = array(
1577
				'post_content' => array(
1578
					'email_message' => $notification['ar_email_message'],
1579
					'email_subject' => isset( $notification['ar_email_subject'] ) ? $notification['ar_email_subject'] : '',
1580
					'email_to'      => $email_field,
1581
					'plain_text'    => isset( $notification['ar_plain_text'] ) ? $notification['ar_plain_text'] : 0,
1582
					'inc_user_info' => 0,
1583
				),
1584
				'post_name'    => $form_id . '_email_' . count( $notifications ),
1585
			);
1586
1587
			$reply_to      = isset( $notification['ar_reply_to'] ) ? $notification['ar_reply_to'] : '';
1588
			$reply_to_name = isset( $notification['ar_reply_to_name'] ) ? $notification['ar_reply_to_name'] : '';
1589
1590
			if ( ! empty( $reply_to ) ) {
1591
				$new_notification2['post_content']['reply_to'] = $reply_to;
1592
			}
1593
1594
			if ( ! empty( $reply_to ) || ! empty( $reply_to_name ) ) {
1595
				$new_notification2['post_content']['from'] = ( empty( $reply_to_name ) ? '[sitename]' : $reply_to_name ) . ' <' . ( empty( $reply_to ) ? '[admin_email]' : $reply_to ) . '>';
1596
			}
1597
1598
			$notifications[] = $new_notification2;
1599
			unset( $new_notification2 );
1600
		}
1601
	}
1602
}
1603
1604