PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.24
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.24
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 / models / FrmFormMigrator.php

FrmFormMigrator.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 6.24, at classes/models/FrmFormMigrator.php

615 lines 15.6 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 abstract class FrmFormMigrator {
7
8 public $source_active;
9
10 public $slug;
11 public $path;
12 public $name;
13
14 public $response = array();
15 public $tracking = 'frm_forms_imported';
16
17 protected $fields_map = array();
18 protected $current_source_form;
19 protected $current_section = array();
20
21 /**
22 * Define required properties.
23 */
24 public function __construct() {
25 if ( ! is_admin() ) {
26 return;
27 }
28
29 if ( ! function_exists( 'is_plugin_active' ) ) {
30 require_once ABSPATH . '/wp-admin/includes/plugin.php';
31 }
32
33 $this->source_active = is_plugin_active( $this->path );
34 if ( ! $this->source_active ) {
35 // if source plugin is not installed, do nothing
36 return;
37 }
38
39 $this->maybe_add_to_import_page();
40
41 $this->response = array(
42 'upgrade_omit' => array(),
43 'unsupported' => array(),
44 );
45 }
46
47 private function maybe_add_to_import_page() {
48 add_action( 'frm_import_settings', array( $this, 'import_page' ) );
49 add_action( 'wp_ajax_frm_import_' . $this->slug, array( $this, 'import_forms' ) );
50 }
51
52 public function import_page() {
53 $forms = $this->get_forms();
54 ?>
55 <div class="wrap">
56 <h2 class="frm-h2"><?php echo esc_html( $this->name ); ?> Importer</h2>
57 <p class="howto">Import forms and settings automatically from <?php echo esc_html( $this->name ); ?>.</p>
58 <div id="welcome-panel">
59 <div style="margin-bottom:10px;">
60 <p class="about-description">
61 Select the forms to import.
62 </p>
63 <form class="frm_form_importer" method="post"
64 action="<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>">
65 <?php wp_nonce_field( 'nonce', 'frm_ajax' ); ?>
66 <input type="hidden" name="slug" value="<?php echo esc_attr( $this->slug ); ?>" />
67 <input type="hidden" name="action" value="frm_import_<?php echo esc_attr( $this->slug ); ?>" />
68 <div style="max-width:400px;text-align:left;">
69 <?php
70 if ( ! $forms ) {
71 esc_html_e( 'No Forms Found.', 'formidable' );
72 }
73 ?>
74 <?php foreach ( $forms as $form_id => $name ) { ?>
75 <p>
76 <label>
77 <input type="checkbox" name="form_id[]"
78 value="<?php echo esc_attr( $form_id ); ?>" checked="checked" />
79 <?php
80 echo esc_html( $name );
81 $new_form_id = $this->is_imported( $form_id );
82 ?>
83 <?php if ( $new_form_id ) { ?>
84 (<a href="<?php echo esc_url( FrmForm::get_edit_link( $new_form_id ) ); ?>">previously imported</a>)
85 <?php } ?>
86 </label>
87 </p>
88 <?php } ?>
89 </div>
90 <?php
91 $button_atts = array(
92 'type' => 'submit',
93 'class' => 'button button-primary frm-button-primary',
94 );
95 if ( ! $forms ) {
96 $button_atts['disabled'] = 'disabled';
97 }
98 ?>
99 <p class="submit">
100 <button <?php FrmAppHelper::array_to_html_params( $button_atts, true ); ?>>
101 <?php esc_html_e( 'Start Import', 'formidable' ); ?>
102 </button>
103 </p>
104 </form>
105 <div id="frm-importer-process" class="frm-importer-process frm_hidden">
106
107 <p class="process-count">
108 <span class="frm-wait" aria-hidden="true"></span>
109 Importing <span class="form-current">1</span> of <span class="form-total">0</span> forms
110 from <?php echo esc_html( $this->name ); ?>.
111 </p>
112
113 <p class="process-completed" class="frm_hidden">
114 <?php
115 printf(
116 // translators: %s is the number of forms that were imported.
117 esc_html__( 'The import process has finished! We have successfully imported %s forms. You can review the results below.', 'formidable' ),
118 '<span class="forms-completed"></span>'
119 );
120 ?>
121 </p>
122
123 <div class="status"></div>
124
125 </div>
126 </div>
127 </div>
128 </div>
129 <?php
130 }
131
132 /**
133 * Import all forms using ajax
134 */
135 public function import_forms() {
136
137 check_ajax_referer( 'frm_ajax', 'nonce' );
138 FrmAppHelper::permission_check( 'frm_edit_forms' );
139
140 $forms = FrmAppHelper::get_simple_request(
141 array(
142 'param' => 'form_id',
143 'type' => 'post',
144 'sanitize' => 'absint',
145 )
146 );
147
148 if ( is_array( $forms ) ) {
149 $imported = array();
150 foreach ( $forms as $form_id ) {
151 $imported[] = $this->import_form( $form_id );
152 }
153 } else {
154 $imported = $this->import_form( $forms );
155 }
156
157 wp_send_json_success( $imported );
158 }
159
160 /**
161 * Import a single form
162 */
163 protected function import_form( $source_id ) {
164
165 $source_form = $this->get_form( $source_id );
166 $source_form_name = $this->get_form_name( $source_form );
167 $source_fields = $this->get_form_fields( $source_form );
168 $this->maybe_add_end_fields( $source_fields );
169
170 $this->current_source_form = $source_form;
171
172 // If form does not contain fields, bail.
173 if ( empty( $source_fields ) ) {
174 wp_send_json_success(
175 array(
176 'error' => true,
177 'name' => esc_html( $source_form_name ),
178 'msg' => __( 'No form fields found.', 'formidable' ),
179 )
180 );
181 }
182
183 $form = $this->prepare_new_form( $source_id, $source_form_name );
184
185 $this->prepare_fields( $source_fields, $form );
186
187 $this->prepare_form( $source_form, $form );
188
189 $response = $this->add_form( $form );
190
191 // reset
192 $this->current_source_form = null;
193
194 return $response;
195 }
196
197 protected function prepare_new_form( $source_id, $source_form_name ) {
198 return array(
199 'import_form_id' => $source_id,
200 'fields' => array(),
201 'name' => $source_form_name,
202 'description' => '',
203 'options' => array(),
204 'actions' => array(),
205 );
206 }
207
208 protected function prepare_form( $form, &$new_form ) {
209 // customize this function
210 }
211
212 protected function prepare_fields( $fields, &$form ) {
213 $field_order = 1;
214
215 foreach ( $fields as $field ) {
216 $field = (array) $field;
217
218 $label = $this->get_field_label( $field );
219 $type = $this->get_field_type( $field );
220
221 // check if field is unsupported. If unsupported make note and continue
222 if ( $this->is_unsupported_field( $type ) ) {
223 $this->response['unsupported'][] = $label;
224 continue;
225 }
226
227 if ( $this->should_skip_field( $type ) ) {
228 $this->response['upgrade_omit'][] = $label;
229 continue;
230 }
231
232 $new_type = $this->convert_field_type( $type, $field );
233
234 $new_field = FrmFieldsHelper::setup_new_vars( $new_type );
235 $new_field['name'] = $label;
236 $new_field['field_order'] = $field_order;
237 $new_field['original'] = $type;
238
239 $this->prepare_field( $field, $new_field );
240
241 $in_section = ! empty( $this->current_section ) && ! in_array( $new_type, $this->fields_with_end() ) && $new_type !== 'break';
242 if ( $in_section ) {
243 $new_field['field_options']['in_section'] = $this->current_section['id'];
244 }
245
246 $form['fields'][] = $new_field;
247
248 if ( in_array( $new_type, $this->fields_with_end() ) ) {
249 $this->current_section = $field;
250 } elseif ( $new_type === 'break' || $new_type === 'end_divider' ) {
251 $this->current_section = array();
252 }
253
254 // This may occasionally skip one level/order e.g. after adding a
255 // list field, as field_order would already be prepared to be used.
256 ++$field_order;
257
258 if ( ! empty( $new_field['fields'] ) && is_array( $new_field['fields'] ) ) {
259 // we have (inner) fields to merge
260
261 $form['fields'] = array_merge( $form['fields'], $new_field['fields'] );
262 // set the new field_order as it would have changed
263 $field_order = $new_field['current_order'];
264 }
265 }//end foreach
266 }
267
268 protected function prepare_field( $field, &$new_field ) {
269 // customize this function
270 }
271
272 /**
273 * Add any field types that will need an end section field.
274 *
275 * @since 4.04.03
276 */
277 protected function fields_with_end() {
278 return array( 'divider' );
279 }
280
281 /**
282 * @since 4.04.03
283 */
284 protected function maybe_add_end_fields( &$fields ) {
285 $with_end = $this->fields_with_end();
286 if ( empty( $with_end ) ) {
287 return;
288 }
289
290 $open = array();
291
292 $order = 0;
293 foreach ( $fields as $field ) {
294 ++$order;
295 $type = $this->get_field_type( $field );
296 $new_type = $this->convert_field_type( $type, $field );
297 if ( ! in_array( $new_type, $with_end ) && $new_type !== 'break' ) {
298 continue;
299 }
300
301 if ( ! empty( $open ) ) {
302 $this->insert_end_section( $fields, $order );
303 $open = array();
304 }
305
306 if ( in_array( $new_type, $with_end ) ) {
307 $open = $field;
308 }
309 }
310
311 if ( ! empty( $open ) ) {
312 $this->insert_end_section( $fields, $order );
313 }
314 }
315
316 /**
317 * @since 4.04.03
318 */
319 protected function insert_end_section( &$fields, &$order ) {
320 $sub = FrmFieldsHelper::setup_new_vars( 'end_divider' );
321 $sub['name'] = __( 'Section Buttons', 'formidable' );
322 $subs = array( $sub );
323 $this->insert_fields_in_array( $subs, $order, 0, $fields );
324 ++$order;
325 }
326
327 /**
328 * Replace the original combo field with a group.
329 * This switches the name field to individual fields.
330 *
331 * @since 4.04.03
332 */
333 protected function insert_fields_in_array( $subs, $start, $remove, &$fields ) {
334 array_splice( $fields, $start, $remove, $subs );
335 }
336
337 /**
338 * @param string $type
339 * @param array $field
340 * @param string $use Which field type to prefer to consider $field as.
341 * This also eases the recursive use of the method,
342 * particularly the overrides in child classes, as
343 * there will be no need to rebuild the converter
344 * array at usage locations.
345 */
346 protected function convert_field_type( $type, $field = array(), $use = '' ) {
347 if ( empty( $field ) ) {
348 // For reverse compatibility.
349 return $type;
350 }
351
352 return $use ? $use : $field['type'];
353 }
354
355 /**
356 * Add the new form to the database and return AJAX data.å
357 *
358 * @param array $form Form to import.
359 * @param array $upgrade_omit No field alternative.
360 */
361 protected function add_form( $form, $upgrade_omit = array() ) {
362
363 // Create empty form so we have an ID to work with.
364 $form_id = $this->create_form( $form );
365
366 if ( empty( $form_id ) ) {
367 return $this->form_creation_error_response( $form );
368 }
369
370 $this->create_fields( $form_id, $form );
371
372 $this->create_emails( $form, $form_id );
373
374 $this->track_import( $form['import_form_id'], $form_id );
375
376 // Build and send final AJAX response!
377 return array(
378 'name' => $form['name'],
379 'id' => $form_id,
380 'link' => esc_url_raw( FrmForm::get_edit_link( $form_id ) ),
381 'upgrade_omit' => $this->response['upgrade_omit'],
382 );
383 }
384
385 /**
386 * @since 4.04.03
387 *
388 * @param array $form parameters for the new form to be created. Only
389 * the name key is a must. The keys are the column
390 * names of the forms table in the DB.
391 *
392 * @return bool|int The ID of the newly created form or false on failure.
393 */
394 protected function create_form( $form ) {
395 $form['form_key'] = $form['name'];
396 $form['status'] = 'published';
397
398 return FrmForm::create( $form );
399 }
400
401 /**
402 * @since 4.04.03
403 */
404 protected function form_creation_error_response( $form ) {
405 return array(
406 'error' => true,
407 'name' => sanitize_text_field( $form['name'] ),
408 'msg' => esc_html__( 'There was an error while creating a new form.', 'formidable' ),
409 );
410 }
411
412 /**
413 * @since 4.04.03
414 *
415 * @param int $form_id
416 * @param array $form
417 */
418 protected function create_fields( $form_id, &$form ) {
419 foreach ( $form['fields'] as $key => $new_field ) {
420 $new_field['form_id'] = $form_id;
421 $form['fields'][ $key ]['id'] = FrmField::create( $new_field );
422 }
423 }
424
425 /**
426 * @since 4.04.03
427 *
428 * @param array $form
429 */
430 protected function create_emails( $form, $form_id ) {
431 foreach ( $form['actions'] as $action ) {
432 $this->save_action( $action, $form, $form_id );
433 }
434 }
435
436 /**
437 * @since 4.04.03
438 *
439 * @param array $action
440 * @param array $form
441 * @param int $form_id
442 */
443 protected function save_action( $action, $form, $form_id ) {
444 /**
445 * @var FrmFormAction
446 */
447 $action_control = FrmFormActionsController::get_form_actions( $action['type'] );
448 unset( $action['type'] );
449 $new_action = $action_control->prepare_new( $form_id );
450 foreach ( $action as $key => $value ) {
451 if ( $key === 'post_title' ) {
452 $new_action->post_title = $value;
453 } elseif ( $key === 'ID' ) {
454 $new_action->ID = $value;
455 } elseif ( $key === 'the_post_title' ) {
456 $new_action->post_content['post_title'] = $value;
457 } elseif ( is_string( $value ) ) {
458 $new_action->post_content[ $key ] = $this->replace_smart_tags( $value, $form['fields'] );
459 } else {
460 $new_action->post_content[ $key ] = $value;
461 }
462 }
463
464 return $action_control->save_settings( $new_action );
465 }
466
467 /**
468 * After a form has been successfully imported we track it, so that in the
469 * future we can alert users if they try to import a form that has already
470 * been imported.
471 *
472 * @param int $source_id Imported plugin form ID.
473 * @param int $new_form_id Formidable form ID.
474 */
475 protected function track_import( $source_id, $new_form_id ) {
476
477 $imported = $this->get_tracked_import();
478
479 $imported[ $this->slug ][ $new_form_id ] = $source_id;
480
481 update_option( $this->tracking, $imported, false );
482 }
483
484 /**
485 * @return array
486 */
487 private function get_tracked_import() {
488 return get_option( $this->tracking, array() );
489 }
490
491 /**
492 * @param int $source_id Imported plugin form ID.
493 *
494 * @return int the ID of the created form or 0
495 */
496 private function is_imported( $source_id ) {
497 $imported = $this->get_tracked_import();
498 $new_form_id = 0;
499 if ( ! isset( $imported[ $this->slug ] ) || ! in_array( $source_id, $imported[ $this->slug ] ) ) {
500 return $new_form_id;
501 }
502
503 $new_form_id = array_search( $source_id, array_reverse( $imported[ $this->slug ], true ) );
504 if ( ! empty( $new_form_id ) && empty( FrmForm::get_key_by_id( $new_form_id ) ) ) {
505 // Allow reimport if the form was deleted.
506 $new_form_id = 0;
507 }
508
509 return $new_form_id;
510 }
511
512 /** Start functions here that should be overridden **/
513
514 /**
515 * @return array
516 */
517 protected function unsupported_field_types() {
518 return array();
519 }
520
521 private function is_unsupported_field( $type ) {
522 $fields = $this->unsupported_field_types();
523
524 return in_array( $type, $fields, true );
525 }
526
527 /**
528 * Strict PRO fields with no Lite alternatives.
529 *
530 * @return array
531 */
532 protected function skip_pro_fields() {
533 return array();
534 }
535
536 protected function should_skip_field( $type ) {
537 $skip_pro_fields = $this->skip_pro_fields();
538
539 return ( ! FrmAppHelper::pro_is_installed() && in_array( $type, $skip_pro_fields, true ) );
540 }
541
542 /**
543 * Replace 3rd-party form provider tags/shortcodes with our own Tags.
544 *
545 * @param string $string String to process the smart tag in.
546 * @param array $fields List of fields for the form.
547 *
548 * @return string
549 */
550 protected function replace_smart_tags( $string, $fields ) {
551 return $string;
552 }
553
554 /**
555 * Get ALL THE FORMS.
556 *
557 * @return array
558 */
559 public function get_forms() {
560 return array();
561 }
562
563 public function get_form( $id ) {
564 return array();
565 }
566
567 /**
568 * @param array|object $source_form
569 *
570 * @return string
571 */
572 protected function get_form_name( $source_form ) {
573 return __( 'Default Form', 'formidable' );
574 }
575
576 /**
577 * @param array|object $source_form
578 *
579 * @return array
580 */
581 protected function get_form_fields( $source_form ) {
582 return array();
583 }
584
585 /**
586 * @param array $field
587 *
588 * @return string
589 */
590 protected function get_field_type( $field ) {
591 return $field['type'];
592 }
593
594 /**
595 * @param array $field
596 *
597 * @return string
598 */
599 protected function get_field_label( $field ) {
600 $label = isset( $field['label'] ) ? $field['label'] : '';
601 if ( ! empty( $label ) ) {
602 return $label;
603 }
604
605 $type = $this->get_field_type( $field );
606 $label = sprintf(
607 /* translators: %1$s - field type */
608 esc_html__( '%1$s Field', 'formidable' ),
609 ucfirst( $type )
610 );
611
612 return trim( $label );
613 }
614 }
615