PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 2.05.09
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v2.05.09
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / helpers / FrmXMLHelper.php

FrmXMLHelper.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 2.05.09, at classes/helpers/FrmXMLHelper.php

1,294 lines 43.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined('ABSPATH') ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmXMLHelper {
7
8 public static function get_xml_values( $opt, $padding ) {
9 if ( is_array( $opt ) ) {
10 foreach ( $opt as $ok => $ov ) {
11 echo "\n" . $padding;
12 $tag = ( is_numeric( $ok ) ? 'key:' : '' ) . $ok;
13 echo '<' . $tag . '>';
14 self::get_xml_values( $ov, $padding . ' ' );
15 if ( is_array( $ov ) ) {
16 echo "\n" . $padding;
17 }
18 echo '</' . $tag . '>';
19 }
20 } else {
21 echo self::cdata( $opt );
22 }
23 }
24
25 public static function import_xml( $file ) {
26 $defaults = array(
27 'forms' => 0,
28 'fields' => 0,
29 'terms' => 0,
30 'posts' => 0,
31 'views' => 0,
32 'actions' => 0,
33 'styles' => 0,
34 );
35
36 $imported = array(
37 'imported' => $defaults,
38 'updated' => $defaults,
39 'forms' => array(),
40 'terms' => array(),
41 );
42
43 unset($defaults);
44
45 if ( ! defined( 'WP_IMPORTING' ) ) {
46 define('WP_IMPORTING', true);
47 }
48
49 if ( ! class_exists( 'DOMDocument' ) ) {
50 return new WP_Error( 'SimpleXML_parse_error', __( 'Your server does not have XML enabled', 'formidable' ), libxml_get_errors() );
51 }
52
53 $dom = new DOMDocument;
54 $success = $dom->loadXML( file_get_contents( $file ) );
55 if ( ! $success ) {
56 return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this XML file', 'formidable' ), libxml_get_errors() );
57 }
58
59 if ( ! function_exists('simplexml_import_dom') ) {
60 return new WP_Error( 'SimpleXML_parse_error', __( 'Your server is missing the simplexml_import_dom function', 'formidable' ), libxml_get_errors() );
61 }
62
63 $xml = simplexml_import_dom( $dom );
64 unset( $dom );
65
66 // halt if loading produces an error
67 if ( ! $xml ) {
68 return new WP_Error( 'SimpleXML_parse_error', __( 'There was an error when reading this XML file', 'formidable' ), libxml_get_errors() );
69 }
70
71 // add terms, forms (form and field ids), posts (post ids), and entries to db, in that order
72 foreach ( array( 'term', 'form', 'view' ) as $item_type ) {
73 // grab cats, tags, and terms, or forms or posts
74 if ( isset($xml->{$item_type} ) ) {
75 $function_name = 'import_xml_' . $item_type . 's';
76 $imported = self::$function_name( $xml->{$item_type}, $imported );
77 unset( $function_name, $xml->{$item_type} );
78 }
79 }
80
81 $return = apply_filters('frm_importing_xml', $imported, $xml );
82
83 return $return;
84 }
85
86 public static function import_xml_terms( $terms, $imported ) {
87 foreach ( $terms as $t ) {
88 if ( term_exists( (string) $t->term_slug, (string) $t->term_taxonomy ) ) {
89 continue;
90 }
91
92 $parent = self::get_term_parent_id( $t );
93
94 $term = wp_insert_term( (string) $t->term_name, (string) $t->term_taxonomy, array(
95 'slug' => (string) $t->term_slug,
96 'description' => (string) $t->term_description,
97 'parent' => empty( $parent ) ? 0 : $parent,
98 ));
99
100 if ( $term && is_array( $term ) ) {
101 $imported['imported']['terms']++;
102 $imported['terms'][ (int) $t->term_id ] = $term['term_id'];
103 }
104
105 unset( $term, $t );
106 }
107
108 return $imported;
109 }
110
111 /**
112 * @since 2.0.8
113 */
114 private static function get_term_parent_id( $t ) {
115 $parent = (string) $t->term_parent;
116 if ( ! empty( $parent ) ) {
117 $parent = term_exists( (string) $t->term_parent, (string) $t->term_taxonomy );
118 if ( $parent ) {
119 $parent = $parent['term_id'];
120 } else {
121 $parent = 0;
122 }
123 }
124 return $parent;
125 }
126
127 public static function import_xml_forms( $forms, $imported ) {
128 $child_forms = array();
129
130 // Import child forms first
131 self::put_child_forms_first( $forms );
132
133 foreach ( $forms as $item ) {
134 $form = self::fill_form( $item );
135
136 self::update_custom_style_setting_on_import( $form );
137
138 $this_form = self::maybe_get_form( $form );
139
140 $old_id = false;
141 $form_fields = false;
142 if ( ! empty( $this_form ) ) {
143 $form_id = $this_form->id;
144 $old_id = $this_form->id;
145 self::update_form( $this_form, $form, $imported );
146
147 $form_fields = self::get_form_fields( $form_id );
148 } else {
149 $form_id = FrmForm::create( $form );
150 if ( $form_id ) {
151 $imported['imported']['forms']++;
152 // Keep track of whether this specific form was updated or not
153 $imported['form_status'][ $form_id ] = 'imported';
154 self::track_imported_child_forms( (int) $form_id, $form['parent_form_id'], $child_forms );
155 }
156 }
157
158 self::import_xml_fields( $item->field, $form_id, $this_form, $form_fields, $imported );
159
160 self::delete_removed_fields( $form_fields );
161
162 // Update field ids/keys to new ones
163 do_action( 'frm_after_duplicate_form', $form_id, $form, array( 'old_id' => $old_id ) );
164
165 $imported['forms'][ (int) $item->id ] = $form_id;
166
167 // Send pre 2.0 form options through function that creates actions
168 self::migrate_form_settings_to_actions( $form['options'], $form_id, $imported, true );
169
170 do_action( 'frm_after_import_form', $form_id, $form );
171
172 unset($form, $item);
173 }
174
175 self::maybe_update_child_form_parent_id( $imported['forms'], $child_forms );
176
177 return $imported;
178 }
179
180 private static function fill_form( $item ) {
181 $form = array(
182 'id' => (int) $item->id,
183 'form_key' => (string) $item->form_key,
184 'name' => (string) $item->name,
185 'description' => (string) $item->description,
186 'options' => (string) $item->options,
187 'logged_in' => (int) $item->logged_in,
188 'is_template' => (int) $item->is_template,
189 'default_template' => (int) $item->default_template,
190 'editable' => (int) $item->editable,
191 'status' => (string) $item->status,
192 'parent_form_id' => isset( $item->parent_form_id ) ? (int) $item->parent_form_id : 0,
193 'created_at' => date( 'Y-m-d H:i:s', strtotime( (string) $item->created_at ) ),
194 );
195 $form['options'] = FrmAppHelper::maybe_json_decode( $form['options'] );
196 return $form;
197 }
198
199 private static function maybe_get_form( $form ) {
200 // if template, allow to edit if form keys match, otherwise, creation date must also match
201 $edit_query = array(
202 'form_key' => $form['form_key'],
203 'is_template' => $form['is_template'],
204 );
205 if ( ! $form['is_template'] ) {
206 $edit_query['created_at'] = $form['created_at'];
207 }
208
209 $edit_query = apply_filters( 'frm_match_xml_form', $edit_query, $form );
210
211 return FrmForm::getAll( $edit_query, '', 1 );
212 }
213
214 private static function update_form( $this_form, $form, &$imported ) {
215 $form_id = $this_form->id;
216 FrmForm::update( $form_id, $form );
217 $imported['updated']['forms']++;
218 // Keep track of whether this specific form was updated or not
219 $imported['form_status'][ $form_id ] = 'updated';
220 }
221
222 private static function get_form_fields( $form_id ) {
223 $form_fields = FrmField::get_all_for_form( $form_id, '', 'exclude', 'exclude' );
224 $old_fields = array();
225 foreach ( $form_fields as $f ) {
226 $old_fields[ $f->id ] = $f;
227 $old_fields[ $f->field_key ] = $f->id;
228 unset($f);
229 }
230 $form_fields = $old_fields;
231 return $form_fields;
232 }
233
234 /**
235 * Delete any fields attached to this form that were not included in the template
236 */
237 private static function delete_removed_fields( $form_fields ) {
238 if ( ! empty( $form_fields ) ) {
239 foreach ( $form_fields as $field ) {
240 if ( is_object( $field ) ) {
241 FrmField::destroy( $field->id );
242 }
243 unset( $field );
244 }
245 }
246 }
247
248 /**
249 * Put child forms first so they will be imported before parents
250 *
251 * @since 2.0.16
252 * @param array $forms
253 */
254 private static function put_child_forms_first( &$forms ) {
255 $child_forms = array();
256 $regular_forms = array();
257
258 foreach ( $forms as $form ) {
259 $parent_form_id = isset( $form->parent_form_id) ? (int) $form->parent_form_id : 0;
260
261 if ( $parent_form_id ) {
262 $child_forms[] = $form;
263 } else {
264 $regular_forms[] = $form;
265 }
266 }
267
268 $forms = array_merge( $child_forms, $regular_forms );
269 }
270
271 /**
272 * Keep track of all imported child forms
273 *
274 * @since 2.0.16
275 * @param int $form_id
276 * @param int $parent_form_id
277 * @param array $child_forms
278 */
279 private static function track_imported_child_forms( $form_id, $parent_form_id, &$child_forms ) {
280 if ( $parent_form_id ) {
281 $child_forms[ $form_id ] = $parent_form_id;
282 }
283 }
284
285 /**
286 * Update the parent_form_id on imported child forms
287 * Child forms are imported first so their parent_form_id will need to be updated after the parent is imported
288 *
289 * @since 2.0.6
290 * @param array $imported_forms
291 * @param array $child_forms
292 */
293 private static function maybe_update_child_form_parent_id( $imported_forms, $child_forms ) {
294 foreach ( $child_forms as $child_form_id => $old_parent_form_id ) {
295
296 if ( isset( $imported_forms[ $old_parent_form_id ] ) && $imported_forms[ $old_parent_form_id ] != $old_parent_form_id ) {
297 // Update all children with this old parent_form_id
298 $new_parent_form_id = (int) $imported_forms[ $old_parent_form_id ];
299
300 FrmForm::update( $child_form_id, array( 'parent_form_id' => $new_parent_form_id ) );
301 }
302 }
303 }
304
305 /**
306 * Import all fields for a form
307 * @since 2.0.13
308 *
309 * TODO: Cut down on params
310 */
311 private static function import_xml_fields( $xml_fields, $form_id, $this_form, &$form_fields, &$imported ) {
312 $in_section = 0;
313
314 foreach ( $xml_fields as $field ) {
315 $f = self::fill_field( $field, $form_id );
316
317 $has_default = array( 'text', 'email', 'url', 'textarea', 'number', 'phone', 'date', 'hidden', 'password', 'tag', 'image' );
318 if ( is_array( $f['default_value'] ) && in_array( $f['type'], $has_default, true ) ) {
319 if ( count( $f['default_value'] ) === 1 ) {
320 $f['default_value'] = '[' . reset( $f['default_value'] ) . ']';
321 } else {
322 $f['default_value'] = reset( $f['default_value'] );
323 }
324 }
325
326 self::maybe_update_in_section_variable( $in_section, $f );
327 self::maybe_update_form_select( $f, $imported );
328 self::maybe_update_get_values_form_setting( $imported, $f );
329
330 if ( ! empty($this_form) ) {
331 // check for field to edit by field id
332 if ( isset( $form_fields[ $f['id'] ] ) ) {
333 FrmField::update( $f['id'], $f );
334 $imported['updated']['fields']++;
335
336 unset( $form_fields[ $f['id'] ] );
337
338 //unset old field key
339 if ( isset( $form_fields[ $f['field_key'] ] ) ) {
340 unset( $form_fields[ $f['field_key'] ] );
341 }
342 } else if ( isset( $form_fields[ $f['field_key'] ] ) ) {
343 // check for field to edit by field key
344 unset($f['id']);
345
346 FrmField::update( $form_fields[ $f['field_key'] ], $f );
347 $imported['updated']['fields']++;
348
349 unset( $form_fields[ $form_fields[ $f['field_key'] ] ] ); //unset old field id
350 unset( $form_fields[ $f['field_key'] ] ); //unset old field key
351 } else {
352 // if no matching field id or key in this form, create the field
353 self::create_imported_field( $f, $imported );
354 }
355 } else {
356
357 self::create_imported_field( $f, $imported );
358 }
359 }
360 }
361
362 private static function fill_field( $field, $form_id ) {
363 return array(
364 'id' => (int) $field->id,
365 'field_key' => (string) $field->field_key,
366 'name' => (string) $field->name,
367 'description' => (string) $field->description,
368 'type' => (string) $field->type,
369 'default_value' => FrmAppHelper::maybe_json_decode( (string) $field->default_value),
370 'field_order' => (int) $field->field_order,
371 'form_id' => (int) $form_id,
372 'required' => (int) $field->required,
373 'options' => FrmAppHelper::maybe_json_decode( (string) $field->options),
374 'field_options' => FrmAppHelper::maybe_json_decode( (string) $field->field_options ),
375 );
376 }
377
378 /**
379 * Update the current in_section value
380 *
381 * @since 2.0.25
382 * @param int $in_section
383 * @param array $f
384 */
385 private static function maybe_update_in_section_variable( &$in_section, &$f ) {
386 // If we're at the end of a section, switch $in_section is 0
387 if ( in_array( $f['type'], array( 'end_divider', 'break', 'form' ) ) ) {
388 $in_section = 0;
389 }
390
391 // Update the current field's in_section value
392 if ( ! isset( $f['field_options']['in_section'] ) ) {
393 $f['field_options']['in_section'] = $in_section;
394 }
395
396 // If we're starting a new section, switch $in_section to ID of divider
397 if ( $f['type'] == 'divider' ) {
398 $in_section = $f['id'];
399 }
400 }
401
402 /**
403 * Switch the form_select on a repeating field or embedded form if it needs to be switched
404 *
405 * @since 2.0.16
406 * @param array $f
407 * @param array $imported
408 */
409 private static function maybe_update_form_select( &$f, $imported ) {
410 if ( ! isset( $imported['forms'] ) ) {
411 return;
412 }
413
414 if ( $f['type'] == 'form' || ( $f['type'] == 'divider' && FrmField::is_option_true( $f['field_options'], 'repeat' ) ) ) {
415 if ( FrmField::is_option_true( $f['field_options'], 'form_select' ) ) {
416 $form_select = (int) $f['field_options']['form_select'];
417 if ( isset( $imported['forms'][ $form_select ] ) ) {
418 $f['field_options']['form_select'] = $imported['forms'][ $form_select ];
419 }
420 }
421 }
422 }
423
424 /**
425 * Update the get_values_form setting if the form was imported
426 *
427 * @since 2.01.0
428 * @param array $imported
429 * @param array $f
430 */
431 private static function maybe_update_get_values_form_setting( $imported, &$f ) {
432 if ( ! isset( $imported['forms'] ) ) {
433 return;
434 }
435
436 if ( FrmField::is_option_true_in_array( $f['field_options'], 'get_values_form' ) ) {
437 $old_form = $f['field_options']['get_values_form'];
438 if ( isset( $imported['forms'][ $old_form ] ) ) {
439 $f['field_options']['get_values_form'] = $imported['forms'][ $old_form ];
440 }
441 }
442 }
443
444 /**
445 * Create an imported field
446 *
447 * @since 2.0.25
448 * @param array $f
449 * @param array $imported
450 */
451 private static function create_imported_field( $f, &$imported ) {
452 $new_id = FrmField::create( $f );
453 if ( $new_id != false ) {
454 $imported['imported']['fields']++;
455 do_action( 'frm_after_field_is_imported', $f, $new_id );
456 }
457 }
458
459 /**
460 * Updates the custom style setting on import
461 * Convert the post slug to an ID
462 *
463 * @since 2.0.19
464 * @param array $form
465 *
466 */
467 private static function update_custom_style_setting_on_import( &$form ) {
468 if ( ! isset( $form['options']['custom_style'] ) ) {
469 return;
470 }
471
472 if ( is_numeric( $form['options']['custom_style'] ) ) {
473 // Set to default
474 $form['options']['custom_style'] = 1;
475 } else {
476 // Replace the style name with the style ID on import
477 global $wpdb;
478 $table = $wpdb->prefix . 'posts';
479 $where = array(
480 'post_name' => $form['options']['custom_style'],
481 'post_type' => 'frm_styles',
482 );
483 $select = 'ID';
484 $style_id = FrmDb::get_var( $table, $where, $select );
485
486 if ( $style_id ) {
487 $form['options']['custom_style'] = $style_id;
488 } else {
489 // save the old style to maybe update after styles import
490 $form['options']['old_style'] = $form['options']['custom_style'];
491
492 // Set to default
493 $form['options']['custom_style'] = 1;
494 }
495 }
496 }
497
498 /**
499 * After styles are imported, check for any forms that were linked
500 * and link them back up.
501 *
502 * @since 2.2.7
503 */
504 private static function update_custom_style_setting_after_import( $form_id ) {
505 $form = FrmForm::getOne( $form_id );
506
507 if ( $form && isset( $form->options['old_style'] ) ) {
508 $form = (array) $form;
509 $saved_style = $form['options']['custom_style'];
510 $form['options']['custom_style'] = $form['options']['old_style'];
511 self::update_custom_style_setting_on_import( $form );
512 $has_changed = ( $form['options']['custom_style'] != $saved_style && $form['options']['custom_style'] != $form['options']['old_style'] );
513 if ( $has_changed ) {
514 FrmForm::update( $form['id'], $form );
515 }
516 }
517 }
518
519 public static function import_xml_views( $views, $imported ) {
520 $imported['posts'] = array();
521 $form_action_type = FrmFormActionsController::$action_post_type;
522
523 $post_types = array(
524 'frm_display' => 'views',
525 $form_action_type => 'actions',
526 'frm_styles' => 'styles',
527 );
528
529 foreach ( $views as $item ) {
530 $post = array(
531 'post_title' => (string) $item->title,
532 'post_name' => (string) $item->post_name,
533 'post_type' => (string) $item->post_type,
534 'post_password' => (string) $item->post_password,
535 'guid' => (string) $item->guid,
536 'post_status' => (string) $item->status,
537 'post_author' => FrmAppHelper::get_user_id_param( (string) $item->post_author ),
538 'post_id' => (int) $item->post_id,
539 'post_parent' => (int) $item->post_parent,
540 'menu_order' => (int) $item->menu_order,
541 'post_content' => FrmFieldsHelper::switch_field_ids( (string) $item->content ),
542 'post_excerpt' => FrmFieldsHelper::switch_field_ids( (string) $item->excerpt ),
543 'is_sticky' => (string) $item->is_sticky,
544 'comment_status' => (string) $item->comment_status,
545 'post_date' => (string) $item->post_date,
546 'post_date_gmt' => (string) $item->post_date_gmt,
547 'ping_status' => (string) $item->ping_status,
548 'postmeta' => array(),
549 'tax_input' => array(),
550 );
551
552 $old_id = $post['post_id'];
553 self::populate_post($post, $item, $imported);
554
555 unset($item);
556
557 $post_id = false;
558 if ( $post['post_type'] == $form_action_type ) {
559 $action_control = FrmFormActionsController::get_form_actions( $post['post_excerpt'] );
560 if ( $action_control && is_object( $action_control ) ) {
561 $post_id = $action_control->maybe_create_action( $post, $imported['form_status'] );
562 }
563 unset($action_control);
564 } else if ( $post['post_type'] == 'frm_styles' ) {
565 // Properly encode post content before inserting the post
566 $post['post_content'] = FrmAppHelper::maybe_json_decode( $post['post_content'] );
567 $custom_css = isset( $post['post_content']['custom_css'] ) ? $post['post_content']['custom_css'] : '';
568 $post['post_content'] = FrmAppHelper::prepare_and_encode( $post['post_content'] );
569
570 // Create/update post now
571 $post_id = wp_insert_post( $post );
572 self::maybe_update_custom_css( $custom_css );
573 } else {
574 // Create/update post now
575 $post_id = wp_insert_post( $post );
576 }
577
578 if ( ! is_numeric($post_id) ) {
579 continue;
580 }
581
582 self::update_postmeta($post, $post_id);
583
584 $this_type = 'posts';
585 if ( isset( $post_types[ $post['post_type'] ] ) ) {
586 $this_type = $post_types[ $post['post_type'] ];
587 }
588
589 if ( isset($post['ID']) && $post_id == $post['ID'] ) {
590 $imported['updated'][ $this_type ]++;
591 } else {
592 $imported['imported'][ $this_type ]++;
593 }
594
595 $imported['posts'][ (int) $old_id ] = $post_id;
596
597 do_action( 'frm_after_import_view', $post_id, $post );
598
599 unset( $post );
600 }
601
602 self::maybe_update_stylesheet( $imported );
603
604 return $imported;
605 }
606
607 private static function populate_post( &$post, $item, $imported ) {
608 if ( isset($item->attachment_url) ) {
609 $post['attachment_url'] = (string) $item->attachment_url;
610 }
611
612 if ( $post['post_type'] == FrmFormActionsController::$action_post_type && isset( $imported['forms'][ (int) $post['menu_order'] ] ) ) {
613 // update to new form id
614 $post['menu_order'] = $imported['forms'][ (int) $post['menu_order'] ];
615 }
616
617 // Don't allow default styles to take over a site's default style
618 if ( 'frm_styles' == $post['post_type'] ) {
619 $post['menu_order'] = 0;
620 }
621
622 foreach ( $item->postmeta as $meta ) {
623 self::populate_postmeta($post, $meta, $imported);
624 unset($meta);
625 }
626
627 self::populate_taxonomies($post, $item);
628
629 self::maybe_editing_post($post);
630 }
631
632 private static function populate_postmeta( &$post, $meta, $imported ) {
633 global $frm_duplicate_ids;
634
635 $m = array(
636 'key' => (string) $meta->meta_key,
637 'value' => (string) $meta->meta_value,
638 );
639
640 //switch old form and field ids to new ones
641 if ( $m['key'] == 'frm_form_id' && isset($imported['forms'][ (int) $m['value'] ]) ) {
642 $m['value'] = $imported['forms'][ (int) $m['value'] ];
643 } else {
644 $m['value'] = FrmAppHelper::maybe_json_decode($m['value']);
645
646 if ( ! empty($frm_duplicate_ids) ) {
647
648 if ( $m['key'] == 'frm_dyncontent' ) {
649 $m['value'] = FrmFieldsHelper::switch_field_ids($m['value']);
650 } else if ( $m['key'] == 'frm_options' ) {
651
652 foreach ( array( 'date_field_id', 'edate_field_id' ) as $setting_name ) {
653 if ( isset( $m['value'][ $setting_name ] ) && is_numeric( $m['value'][ $setting_name ] ) && isset( $frm_duplicate_ids[ $m['value'][ $setting_name ] ] ) ) {
654 $m['value'][ $setting_name ] = $frm_duplicate_ids[ $m['value'][ $setting_name ] ];
655 }
656 }
657
658 $check_dup_array = array();
659 if ( isset( $m['value']['order_by'] ) && ! empty( $m['value']['order_by'] ) ) {
660 if ( is_numeric( $m['value']['order_by'] ) && isset( $frm_duplicate_ids[ $m['value']['order_by'] ] ) ) {
661 $m['value']['order_by'] = $frm_duplicate_ids[ $m['value']['order_by'] ];
662 } else if ( is_array( $m['value']['order_by'] ) ) {
663 $check_dup_array[] = 'order_by';
664 }
665 }
666
667 if ( isset( $m['value']['where'] ) && ! empty( $m['value']['where'] ) ) {
668 $check_dup_array[] = 'where';
669 }
670
671 foreach ( $check_dup_array as $check_k ) {
672 foreach ( (array) $m['value'][ $check_k ] as $mk => $mv ) {
673 if ( isset( $frm_duplicate_ids[ $mv ] ) ) {
674 $m['value'][ $check_k ][ $mk ] = $frm_duplicate_ids[ $mv ];
675 }
676 unset($mk, $mv);
677 }
678 }
679 }
680 }
681 }
682
683 if ( ! is_array($m['value']) ) {
684 $m['value'] = FrmAppHelper::maybe_json_decode($m['value']);
685 }
686
687 $post['postmeta'][ (string) $meta->meta_key ] = $m['value'];
688 }
689
690 /**
691 * Add terms to post
692 * @param array $post by reference
693 * @param object $item The XML object data
694 */
695 private static function populate_taxonomies( &$post, $item ) {
696 foreach ( $item->category as $c ) {
697 $att = $c->attributes();
698 if ( ! isset( $att['nicename'] ) ) {
699 continue;
700 }
701
702 $taxonomy = (string) $att['domain'];
703 if ( is_taxonomy_hierarchical($taxonomy) ) {
704 $name = (string) $att['nicename'];
705 $h_term = get_term_by('slug', $name, $taxonomy);
706 if ( $h_term ) {
707 $name = $h_term->term_id;
708 }
709 unset($h_term);
710 } else {
711 $name = (string) $c;
712 }
713
714 if ( ! isset( $post['tax_input'][ $taxonomy ] ) ) {
715 $post['tax_input'][ $taxonomy ] = array();
716 }
717
718 $post['tax_input'][ $taxonomy ][] = $name;
719 unset($name);
720 }
721 }
722
723 /**
724 * Edit post if the key and created time match
725 */
726 private static function maybe_editing_post( &$post ) {
727 $match_by = array(
728 'post_type' => $post['post_type'],
729 'name' => $post['post_name'],
730 'post_status' => $post['post_status'],
731 'posts_per_page' => 1,
732 );
733
734 if ( in_array( $post['post_status'], array( 'trash', 'draft' ) ) ) {
735 $match_by['include'] = $post['post_id'];
736 unset($match_by['name']);
737 }
738
739 $editing = get_posts($match_by);
740
741 if ( ! empty($editing) && current($editing)->post_date == $post['post_date'] ) {
742 // set the id of the post to edit
743 $post['ID'] = current($editing)->ID;
744 }
745 }
746
747 private static function update_postmeta( &$post, $post_id ) {
748 foreach ( $post['postmeta'] as $k => $v ) {
749 if ( '_edit_last' == $k ) {
750 $v = FrmAppHelper::get_user_id_param($v);
751 } else if ( '_thumbnail_id' == $k && FrmAppHelper::pro_is_installed() ) {
752 //change the attachment ID
753 $v = FrmProXMLHelper::get_file_id($v);
754 }
755
756 update_post_meta($post_id, $k, $v);
757
758 unset($k, $v);
759 }
760 }
761
762 /**
763 * If a template includes custom css, let's include it.
764 * The custom css is included on the default style.
765 *
766 * @since 2.03
767 */
768 private static function maybe_update_custom_css( $custom_css ) {
769 if ( empty( $custom_css ) ) {
770 return;
771 }
772
773 $frm_style = new FrmStyle();
774 $default_style = $frm_style->get_default_style();
775 $default_style->post_content['custom_css'] .= "\r\n\r\n" . $custom_css;
776 $frm_style->save( $default_style );
777 }
778
779 private static function maybe_update_stylesheet( $imported ) {
780 $new_styles = isset( $imported['imported']['styles'] ) && ! empty( $imported['imported']['styles'] );
781 $updated_styles = isset( $imported['updated']['styles'] ) && ! empty( $imported['updated']['styles'] );
782 if ( $new_styles || $updated_styles ) {
783 if ( is_admin() && function_exists( 'get_filesystem_method' ) ) {
784 $frm_style = new FrmStyle();
785 $frm_style->update( 'default' );
786 }
787 foreach ( $imported['forms'] as $form_id ) {
788 self::update_custom_style_setting_after_import( $form_id );
789 }
790 }
791 }
792
793 /**
794 * @param string $message
795 */
796 public static function parse_message( $result, &$message, &$errors ) {
797 if ( is_wp_error($result) ) {
798 $errors[] = $result->get_error_message();
799 } else if ( ! $result ) {
800 return;
801 }
802
803 if ( ! is_array($result) ) {
804 $message = is_string( $result ) ? $result : htmlentities( print_r( $result, 1 ) );
805 return;
806 }
807
808 $t_strings = array(
809 'imported' => __( 'Imported', 'formidable' ),
810 'updated' => __( 'Updated', 'formidable' ),
811 );
812
813 $message = '<ul>';
814 foreach ( $result as $type => $results ) {
815 if ( ! isset( $t_strings[ $type ] ) ) {
816 // only print imported and updated
817 continue;
818 }
819
820 $s_message = array();
821 foreach ( $results as $k => $m ) {
822 self::item_count_message($m, $k, $s_message);
823 unset($k, $m);
824 }
825
826 if ( ! empty($s_message) ) {
827 $message .= '<li><strong>' . $t_strings[ $type ] . ':</strong> ';
828 $message .= implode(', ', $s_message);
829 $message .= '</li>';
830 }
831 }
832
833 if ( $message == '<ul>' ) {
834 $message = '';
835 $errors[] = __( 'Nothing was imported or updated', 'formidable' );
836 } else {
837 $message .= '</ul>';
838 }
839 }
840
841 public static function item_count_message( $m, $type, &$s_message ) {
842 if ( ! $m ) {
843 return;
844 }
845
846 $strings = array(
847 'forms' => sprintf( _n( '%1$s Form', '%1$s Forms', $m, 'formidable' ), $m ),
848 'fields' => sprintf( _n( '%1$s Field', '%1$s Fields', $m, 'formidable' ), $m ),
849 'items' => sprintf( _n( '%1$s Entry', '%1$s Entries', $m, 'formidable' ), $m ),
850 'views' => sprintf( _n( '%1$s View', '%1$s Views', $m, 'formidable' ), $m ),
851 'posts' => sprintf( _n( '%1$s Post', '%1$s Posts', $m, 'formidable' ), $m ),
852 'styles' => sprintf( _n( '%1$s Style', '%1$s Styles', $m, 'formidable' ), $m ),
853 'terms' => sprintf( _n( '%1$s Term', '%1$s Terms', $m, 'formidable' ), $m ),
854 'actions' => sprintf( _n( '%1$s Form Action', '%1$s Form Actions', $m, 'formidable' ), $m ),
855 );
856
857 $s_message[] = isset( $strings[ $type ] ) ? $strings[ $type ] : ' ' . $m . ' ' . ucfirst( $type );
858 }
859
860 /**
861 * Prepare the form options for export
862 *
863 * @since 2.0.19
864 * @param string $options
865 * @return string
866 */
867 public static function prepare_form_options_for_export( $options ) {
868 $options = maybe_unserialize( $options );
869 // Change custom_style to the post_name instead of ID
870 if ( isset( $options['custom_style'] ) && 1 !== $options['custom_style'] ) {
871 global $wpdb;
872 $table = $wpdb->prefix . 'posts';
873 $where = array( 'ID' => $options['custom_style'] );
874 $select = 'post_name';
875
876 $style_name = FrmDb::get_var( $table, $where, $select );
877
878 if ( $style_name ) {
879 $options['custom_style'] = $style_name;
880 } else {
881 $options['custom_style'] = 1;
882 }
883 }
884 $options = serialize( $options );
885 return self::cdata( $options );
886 }
887
888 public static function cdata( $str ) {
889 $str = maybe_unserialize($str);
890 if ( is_array($str) ) {
891 $str = json_encode($str);
892 } else if ( seems_utf8( $str ) == false ) {
893 $str = utf8_encode( $str );
894 }
895
896 if ( is_numeric($str) ) {
897 return $str;
898 }
899
900 self::remove_invalid_characters_from_xml( $str );
901
902 // $str = ent2ncr(esc_html($str));
903 $str = '<![CDATA[' . str_replace( ']]>', ']]]]><![CDATA[>', $str ) . ']]>';
904
905 return $str;
906 }
907
908 /**
909 * Remove <US> character (unit separator) from exported strings
910 *
911 * @since 2.0.22
912 * @param string $str
913 */
914 private static function remove_invalid_characters_from_xml( &$str ) {
915 // Remove <US> character
916 $str = str_replace( '\x1F', '', $str );
917 }
918
919 public static function migrate_form_settings_to_actions( $form_options, $form_id, &$imported = array(), $switch = false ) {
920 // Get post type
921 $post_type = FrmFormActionsController::$action_post_type;
922
923 // Set up imported index, if not set up yet
924 if ( ! isset( $imported['imported']['actions'] ) ) {
925 $imported['imported']['actions'] = 0;
926 }
927
928 // Migrate post settings to action
929 self::migrate_post_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
930
931 // Migrate email settings to action
932 self::migrate_email_settings_to_action( $form_options, $form_id, $post_type, $imported, $switch );
933 }
934
935 /**
936 * Migrate post settings to form action
937 *
938 * @param string $post_type
939 */
940 private static function migrate_post_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
941 if ( ! isset($form_options['create_post']) || ! $form_options['create_post'] ) {
942 return;
943 }
944
945 $new_action = array(
946 'post_type' => $post_type,
947 'post_excerpt' => 'wppost',
948 'post_title' => __( 'Create Posts', 'formidable' ),
949 'menu_order' => $form_id,
950 'post_status' => 'publish',
951 'post_content' => array(),
952 'post_name' => $form_id . '_wppost_1',
953 );
954
955 $post_settings = array( 'post_type', 'post_category', 'post_content', 'post_excerpt', 'post_title', 'post_name', 'post_date', 'post_status', 'post_custom_fields', 'post_password' );
956
957 foreach ( $post_settings as $post_setting ) {
958 if ( isset( $form_options[ $post_setting ] ) ) {
959 $new_action['post_content'][ $post_setting ] = $form_options[ $post_setting ];
960 }
961 unset($post_setting);
962 }
963
964 $new_action['event'] = array( 'create', 'update' );
965
966 if ( $switch ) {
967 // Fields with string or int saved
968 $basic_fields = array( 'post_title', 'post_content', 'post_excerpt', 'post_password', 'post_date', 'post_status' );
969
970 // Fields with arrays saved
971 $array_fields = array( 'post_category', 'post_custom_fields' );
972
973 $new_action['post_content'] = self::switch_action_field_ids( $new_action['post_content'], $basic_fields, $array_fields );
974 }
975 $new_action['post_content'] = json_encode($new_action['post_content']);
976
977 $exists = get_posts( array(
978 'name' => $new_action['post_name'],
979 'post_type' => $new_action['post_type'],
980 'post_status' => $new_action['post_status'],
981 'numberposts' => 1,
982 ) );
983
984 if ( ! $exists ) {
985 // this isn't an email, but we need to use a class that will always be included
986 FrmDb::save_json_post( $new_action );
987 $imported['imported']['actions']++;
988 }
989 }
990
991 /**
992 * Switch old field IDs for new field IDs in emails and post
993 *
994 * @since 2.0
995 * @param array $post_content - check for old field IDs
996 * @param array $basic_fields - fields with string or int saved
997 * @param array $array_fields - fields with arrays saved
998 *
999 * @return string $post_content - new field IDs
1000 */
1001 private static function switch_action_field_ids( $post_content, $basic_fields, $array_fields = array() ) {
1002 global $frm_duplicate_ids;
1003
1004 // If there aren't IDs that were switched, end now
1005 if ( ! $frm_duplicate_ids ) {
1006 return;
1007 }
1008
1009 // Get old IDs
1010 $old = array_keys( $frm_duplicate_ids );
1011
1012 // Get new IDs
1013 $new = array_values( $frm_duplicate_ids );
1014
1015 // Do a str_replace with each item to set the new IDs
1016 foreach ( $post_content as $key => $setting ) {
1017 if ( ! is_array( $setting ) && in_array( $key, $basic_fields ) ) {
1018 // Replace old IDs with new IDs
1019 $post_content[ $key ] = str_replace( $old, $new, $setting );
1020 } else if ( is_array( $setting ) && in_array( $key, $array_fields ) ) {
1021 foreach ( $setting as $k => $val ) {
1022 // Replace old IDs with new IDs
1023 $post_content[ $key ][ $k ] = str_replace( $old, $new, $val );
1024 }
1025 }
1026 unset( $key, $setting );
1027 }
1028 return $post_content;
1029 }
1030
1031 private static function migrate_email_settings_to_action( $form_options, $form_id, $post_type, &$imported, $switch ) {
1032 // No old notifications or autoresponders to carry over
1033 if ( ! isset( $form_options['auto_responder'] ) && ! isset( $form_options['notification'] ) && ! isset( $form_options['email_to'] ) ) {
1034 return;
1035 }
1036
1037 // Initialize notifications array
1038 $notifications = array();
1039
1040 // Migrate regular notifications
1041 self::migrate_notifications_to_action( $form_options, $form_id, $notifications );
1042
1043 // Migrate autoresponders
1044 self::migrate_autoresponder_to_action( $form_options, $form_id, $notifications );
1045
1046 if ( empty( $notifications ) ) {
1047 return;
1048 }
1049
1050 foreach ( $notifications as $new_notification ) {
1051 $new_notification['post_type'] = $post_type;
1052 $new_notification['post_excerpt'] = 'email';
1053 $new_notification['post_title'] = __( 'Email Notification', 'formidable' );
1054 $new_notification['menu_order'] = $form_id;
1055 $new_notification['post_status'] = 'publish';
1056
1057 // Switch field IDs and keys, if needed
1058 if ( $switch ) {
1059
1060 // Switch field IDs in email conditional logic
1061 self::switch_email_contition_field_ids( $new_notification['post_content'] );
1062
1063 // Switch all other field IDs in email
1064 $new_notification['post_content'] = FrmFieldsHelper::switch_field_ids( $new_notification['post_content'] );
1065 }
1066 $new_notification['post_content'] = FrmAppHelper::prepare_and_encode( $new_notification['post_content'] );
1067
1068 $exists = get_posts( array(
1069 'name' => $new_notification['post_name'],
1070 'post_type' => $new_notification['post_type'],
1071 'post_status' => $new_notification['post_status'],
1072 'numberposts' => 1,
1073 ) );
1074
1075 if ( empty($exists) ) {
1076 FrmDb::save_json_post( $new_notification );
1077 $imported['imported']['actions']++;
1078 }
1079 unset($new_notification);
1080 }
1081
1082 self::remove_deprecated_notification_settings( $form_id, $form_options );
1083 }
1084
1085 /**
1086 * Remove deprecated notification settings after migration
1087 *
1088 * @since 2.05
1089 *
1090 * @param int|string $form_id
1091 * @param array $form_options
1092 */
1093 private static function remove_deprecated_notification_settings( $form_id, $form_options ) {
1094 $delete_settings = array( 'notification', 'autoresponder', 'email_to' );
1095 foreach ( $delete_settings as $index ) {
1096 if ( isset( $form_options[ $index ] ) ) {
1097 unset( $form_options[ $index ] );
1098 }
1099 }
1100 FrmForm::update( $form_id, array( 'options' => $form_options ) );
1101 }
1102
1103 private static function migrate_notifications_to_action( $form_options, $form_id, &$notifications ) {
1104 if ( ! isset( $form_options['notification'] ) && isset( $form_options['email_to'] ) && ! empty( $form_options['email_to'] ) ) {
1105 // add old settings into notification array
1106 $form_options['notification'] = array( 0 => $form_options );
1107 } else if ( isset( $form_options['notification']['email_to'] ) ) {
1108 // make sure it's in the correct format
1109 $form_options['notification'] = array( 0 => $form_options['notification'] );
1110 }
1111
1112 if ( isset( $form_options['notification'] ) && is_array($form_options['notification']) ) {
1113 foreach ( $form_options['notification'] as $email_key => $notification ) {
1114
1115 $atts = array(
1116 'email_to' => '',
1117 'reply_to' => '',
1118 'reply_to_name' => '',
1119 'event' => '',
1120 'form_id' => $form_id,
1121 'email_key' => $email_key,
1122 );
1123
1124 // Format the email data
1125 self::format_email_data( $atts, $notification );
1126
1127 if ( isset( $notification['twilio'] ) && $notification['twilio'] ) {
1128 do_action( 'frm_create_twilio_action', $atts, $notification );
1129 }
1130
1131 // Setup the new notification
1132 $new_notification = array();
1133 self::setup_new_notification( $new_notification, $notification, $atts );
1134
1135 $notifications[] = $new_notification;
1136 }
1137 }
1138 }
1139
1140 private static function format_email_data( &$atts, $notification ) {
1141 // Format email_to
1142 self::format_email_to_data( $atts, $notification );
1143
1144 // Format the reply to email and name
1145 $reply_fields = array(
1146 'reply_to' => '',
1147 'reply_to_name' => '',
1148 );
1149 foreach ( $reply_fields as $f => $val ) {
1150 if ( isset( $notification[ $f ] ) ) {
1151 $atts[ $f ] = $notification[ $f ];
1152 if ( 'custom' == $notification[ $f ] ) {
1153 $atts[ $f ] = $notification[ 'cust_' . $f ];
1154 } else if ( is_numeric( $atts[ $f ] ) && ! empty( $atts[ $f ] ) ) {
1155 $atts[ $f ] = '[' . $atts[ $f ] . ']';
1156 }
1157 }
1158 unset( $f, $val );
1159 }
1160
1161 // Format event
1162 $atts['event'] = array( 'create' );
1163 if ( isset( $notification['update_email'] ) && 1 == $notification['update_email'] ) {
1164 $atts['event'][] = 'update';
1165 } else if ( isset($notification['update_email']) && 2 == $notification['update_email'] ) {
1166 $atts['event'] = array( 'update' );
1167 }
1168 }
1169
1170 private static function format_email_to_data( &$atts, $notification ) {
1171 if ( isset( $notification['email_to'] ) ) {
1172 $atts['email_to'] = preg_split( '/ (,|;) /', $notification['email_to'] );
1173 } else {
1174 $atts['email_to'] = array();
1175 }
1176
1177 if ( isset( $notification['also_email_to'] ) ) {
1178 $email_fields = (array) $notification['also_email_to'];
1179 $atts['email_to'] = array_merge( $email_fields, $atts['email_to'] );
1180 unset( $email_fields );
1181 }
1182
1183 foreach ( $atts['email_to'] as $key => $email_field ) {
1184
1185 if ( is_numeric( $email_field ) ) {
1186 $atts['email_to'][ $key ] = '[' . $email_field . ']';
1187 }
1188
1189 if ( strpos( $email_field, '|') ) {
1190 $email_opt = explode( '|', $email_field );
1191 if ( isset( $email_opt[0] ) ) {
1192 $atts['email_to'][ $key ] = '[' . $email_opt[0] . ' show=' . $email_opt[1] . ']';
1193 }
1194 unset( $email_opt );
1195 }
1196 }
1197 $atts['email_to'] = implode(', ', $atts['email_to']);
1198 }
1199
1200 private static function setup_new_notification( &$new_notification, $notification, $atts ) {
1201 // Set up new notification
1202 $new_notification = array(
1203 'post_content' => array(
1204 'email_to' => $atts['email_to'],
1205 'event' => $atts['event'],
1206 ),
1207 'post_name' => $atts['form_id'] . '_email_' . $atts['email_key'],
1208 );
1209
1210 // Add more fields to the new notification
1211 $add_fields = array( 'email_message', 'email_subject', 'plain_text', 'inc_user_info', 'conditions' );
1212 foreach ( $add_fields as $add_field ) {
1213 if ( isset( $notification[ $add_field ] ) ) {
1214 $new_notification['post_content'][ $add_field ] = $notification[ $add_field ];
1215 } else if ( in_array( $add_field, array( 'plain_text', 'inc_user_info' ) ) ) {
1216 $new_notification['post_content'][ $add_field ] = 0;
1217 } else {
1218 $new_notification['post_content'][ $add_field ] = '';
1219 }
1220 unset( $add_field );
1221 }
1222
1223 // Set reply to
1224 $new_notification['post_content']['reply_to'] = $atts['reply_to'];
1225
1226 // Set from
1227 if ( ! empty( $atts['reply_to'] ) || ! empty( $atts['reply_to_name'] ) ) {
1228 $new_notification['post_content']['from'] = ( empty( $atts['reply_to_name'] ) ? '[sitename]' : $atts['reply_to_name'] ) . ' <' . ( empty( $atts['reply_to'] ) ? '[admin_email]' : $atts['reply_to'] ) . '>';
1229 }
1230 }
1231
1232 /**
1233 * Switch field IDs in pre-2.0 email conditional logic
1234 *
1235 * @param $post_content array, pass by reference
1236 */
1237 private static function switch_email_contition_field_ids( &$post_content ) {
1238 // Switch field IDs in conditional logic
1239 if ( isset( $post_content['conditions'] ) && is_array( $post_content['conditions'] ) ) {
1240 foreach ( $post_content['conditions'] as $email_key => $val ) {
1241 if ( is_numeric( $email_key ) ) {
1242 $post_content['conditions'][ $email_key ] = self::switch_action_field_ids( $val, array( 'hide_field' ) );
1243 }
1244 unset( $email_key, $val);
1245 }
1246 }
1247 }
1248
1249 private static function migrate_autoresponder_to_action( $form_options, $form_id, &$notifications ) {
1250 if ( isset($form_options['auto_responder']) && $form_options['auto_responder'] && isset($form_options['ar_email_message']) && $form_options['ar_email_message'] ) {
1251 // migrate autoresponder
1252
1253 $email_field = isset($form_options['ar_email_to']) ? $form_options['ar_email_to'] : 0;
1254 if ( strpos($email_field, '|') ) {
1255 // data from entries field
1256 $email_field = explode('|', $email_field);
1257 if ( isset($email_field[1]) ) {
1258 $email_field = $email_field[1];
1259 }
1260 }
1261 if ( is_numeric($email_field) && ! empty($email_field) ) {
1262 $email_field = '[' . $email_field . ']';
1263 }
1264
1265 $notification = $form_options;
1266 $new_notification2 = array(
1267 'post_content' => array(
1268 'email_message' => $notification['ar_email_message'],
1269 'email_subject' => isset($notification['ar_email_subject']) ? $notification['ar_email_subject'] : '',
1270 'email_to' => $email_field,
1271 'plain_text' => isset($notification['ar_plain_text']) ? $notification['ar_plain_text'] : 0,
1272 'inc_user_info' => 0,
1273 ),
1274 'post_name' => $form_id . '_email_' . count( $notifications ),
1275 );
1276
1277 $reply_to = isset($notification['ar_reply_to']) ? $notification['ar_reply_to'] : '';
1278 $reply_to_name = isset($notification['ar_reply_to_name']) ? $notification['ar_reply_to_name'] : '';
1279
1280 if ( ! empty( $reply_to ) ) {
1281 $new_notification2['post_content']['reply_to'] = $reply_to;
1282 }
1283
1284 if ( ! empty( $reply_to ) || ! empty( $reply_to_name ) ) {
1285 $new_notification2['post_content']['from'] = ( empty( $reply_to_name ) ? '[sitename]' : $reply_to_name ) . ' <' . ( empty( $reply_to ) ? '[admin_email]' : $reply_to ) . '>';
1286 }
1287
1288 $notifications[] = $new_notification2;
1289 unset( $new_notification2 );
1290 }
1291 }
1292 }
1293
1294