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

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