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

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