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

593 lines 15.1 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 occassionally 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 compatability.
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 $action_control = FrmFormActionsController::get_form_actions( $action['type'] );
426 unset( $action['type'] );
427 $new_action = $action_control->prepare_new( $form_id );
428 foreach ( $action as $key => $value ) {
429 if ( $key === 'post_title' ) {
430 $new_action->post_title = $value;
431 } elseif ( $key === 'ID' ) {
432 $new_action->ID = $value;
433 } elseif ( $key === 'the_post_title' ) {
434 $new_action->post_content['post_title'] = $value;
435 } elseif ( is_string( $value ) ) {
436 $new_action->post_content[ $key ] = $this->replace_smart_tags( $value, $form['fields'] );
437 } else {
438 $new_action->post_content[ $key ] = $value;
439 }
440 }
441
442 return $action_control->save_settings( $new_action );
443 }
444
445 /**
446 * After a form has been successfully imported we track it, so that in the
447 * future we can alert users if they try to import a form that has already
448 * been imported.
449 *
450 * @param int $source_id Imported plugin form ID.
451 * @param int $new_form_id Formidable form ID.
452 */
453 protected function track_import( $source_id, $new_form_id ) {
454
455 $imported = $this->get_tracked_import();
456
457 $imported[ $this->slug ][ $new_form_id ] = $source_id;
458
459 update_option( $this->tracking, $imported, false );
460 }
461
462 /**
463 * @return array
464 */
465 private function get_tracked_import() {
466 return get_option( $this->tracking, array() );
467 }
468
469 /**
470 * @param int $source_id Imported plugin form ID.
471 *
472 * @return int the ID of the created form or 0
473 */
474 private function is_imported( $source_id ) {
475 $imported = $this->get_tracked_import();
476 $new_form_id = 0;
477 if ( ! isset( $imported[ $this->slug ] ) || ! in_array( $source_id, $imported[ $this->slug ] ) ) {
478 return $new_form_id;
479 }
480
481 $new_form_id = array_search( $source_id, array_reverse( $imported[ $this->slug ], true ) );
482 if ( ! empty( $new_form_id ) && empty( FrmForm::get_key_by_id( $new_form_id ) ) ) {
483 // Allow reimport if the form was deleted.
484 $new_form_id = 0;
485 }
486
487 return $new_form_id;
488 }
489
490 /** Start functions here that should be overridden **/
491
492 /**
493 * @return array
494 */
495 protected function unsupported_field_types() {
496 return array();
497 }
498
499 private function is_unsupported_field( $type ) {
500 $fields = $this->unsupported_field_types();
501
502 return in_array( $type, $fields, true );
503 }
504
505 /**
506 * Strict PRO fields with no Lite alternatives.
507 *
508 * @return array
509 */
510 protected function skip_pro_fields() {
511 return array();
512 }
513
514 protected function should_skip_field( $type ) {
515 $skip_pro_fields = $this->skip_pro_fields();
516
517 return ( ! FrmAppHelper::pro_is_installed() && in_array( $type, $skip_pro_fields, true ) );
518 }
519
520 /**
521 * Replace 3rd-party form provider tags/shortcodes with our own Tags.
522 *
523 * @param string $string String to process the smart tag in.
524 * @param array $fields List of fields for the form.
525 *
526 * @return string
527 */
528 protected function replace_smart_tags( $string, $fields ) {
529 return $string;
530 }
531
532 /**
533 * Get ALL THE FORMS.
534 *
535 * @return array
536 */
537 public function get_forms() {
538 return array();
539 }
540
541 public function get_form( $id ) {
542 return array();
543 }
544
545 /**
546 * @param array|object $source_form
547 *
548 * @return string
549 */
550 protected function get_form_name( $source_form ) {
551 return __( 'Default Form', 'formidable' );
552 }
553
554 /**
555 * @param array|object $source_form
556 *
557 * @return array
558 */
559 protected function get_form_fields( $source_form ) {
560 return array();
561 }
562
563 /**
564 * @param array $field
565 *
566 * @return string
567 */
568 protected function get_field_type( $field ) {
569 return $field['type'];
570 }
571
572 /**
573 * @param array $field
574 *
575 * @return string
576 */
577 protected function get_field_label( $field ) {
578 $label = isset( $field['label'] ) ? $field['label'] : '';
579 if ( ! empty( $label ) ) {
580 return $label;
581 }
582
583 $type = $this->get_field_type( $field );
584 $label = sprintf(
585 /* translators: %1$s - field type */
586 esc_html__( '%1$s Field', 'formidable' ),
587 ucfirst( $type )
588 );
589
590 return trim( $label );
591 }
592 }
593