Completed
Push — master ( cf8846...7515aa )
by Stephanie
16s queued 11s
created

FrmXMLHelper::migrate_notifications_to_action()   B

Complexity

Conditions 10
Paths 12

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

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