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

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