PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.16.3
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.16.3
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 / FrmMigrate.php

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

687 lines 19.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 class FrmMigrate {
7 public $fields;
8 public $forms;
9 public $entries;
10 public $entry_metas;
11
12 public function __construct() {
13 global $wpdb;
14 $this->fields = $wpdb->prefix . 'frm_fields';
15 $this->forms = $wpdb->prefix . 'frm_forms';
16 $this->entries = $wpdb->prefix . 'frm_items';
17 $this->entry_metas = $wpdb->prefix . 'frm_item_metas';
18 }
19
20 public function upgrade() {
21 do_action( 'frm_before_install' );
22
23 global $wpdb, $frm_vars;
24
25 $frm_vars['doing_upgrade'] = true;
26
27 $needs_upgrade = FrmAppController::compare_for_update(
28 array(
29 'option' => 'frm_db_version',
30 'new_db_version' => FrmAppHelper::$db_version,
31 'new_plugin_version' => FrmAppHelper::plugin_version(),
32 )
33 );
34
35 if ( $needs_upgrade ) {
36 // update rewrite rules for views and other custom post types
37 flush_rewrite_rules();
38
39 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
40
41 $old_db_version = get_option( 'frm_db_version' );
42
43 $this->create_tables();
44 $this->migrate_data( $old_db_version );
45
46 // SAVE DB VERSION.
47 update_option( 'frm_db_version', FrmAppHelper::plugin_version() . '-' . FrmAppHelper::$db_version );
48
49 if ( ! $old_db_version ) {
50 $this->maybe_create_contact_form();
51
52 if ( false === get_option( 'frm_first_activation' ) ) {
53 update_option( 'frm_first_activation', time(), false );
54 }
55 }
56 }//end if
57
58 do_action( 'frm_after_install' );
59
60 $frm_vars['doing_upgrade'] = false;
61
62 FrmAppHelper::save_combined_js();
63
64 // update the styling settings
65 if ( function_exists( 'get_filesystem_method' ) ) {
66 $frm_style = new FrmStyle();
67 $frm_style->update( 'default' );
68 }
69 }
70
71 public function collation() {
72 global $wpdb;
73 if ( ! $wpdb->has_cap( 'collation' ) ) {
74 return '';
75 }
76
77 return $wpdb->get_charset_collate();
78 }
79
80 private function create_tables() {
81 $charset_collate = $this->collation();
82 $sql = array();
83
84 /* Create/Upgrade Fields Table */
85 $sql[] = 'CREATE TABLE ' . $this->fields . ' (
86 id BIGINT(20) NOT NULL auto_increment,
87 field_key varchar(100) default NULL,
88 name text default NULL,
89 description longtext default NULL,
90 type text default NULL,
91 default_value longtext default NULL,
92 options longtext default NULL,
93 field_order int(11) default 0,
94 required int(1) default NULL,
95 field_options longtext default NULL,
96 form_id int(11) default NULL,
97 created_at datetime NOT NULL,
98 PRIMARY KEY (id),
99 KEY form_id (form_id),
100 UNIQUE KEY field_key (field_key)
101 )';
102
103 /* Create/Upgrade Forms Table */
104 $sql[] = 'CREATE TABLE ' . $this->forms . ' (
105 id int(11) NOT NULL auto_increment,
106 form_key varchar(100) default NULL,
107 name varchar(255) default NULL,
108 description text default NULL,
109 parent_form_id int(11) default 0,
110 logged_in tinyint(1) default NULL,
111 editable tinyint(1) default NULL,
112 is_template tinyint(1) default 0,
113 default_template tinyint(1) default 0,
114 status varchar(255) default NULL,
115 options longtext default NULL,
116 created_at datetime NOT NULL,
117 PRIMARY KEY (id),
118 UNIQUE KEY form_key (form_key)
119 )';
120
121 /* Create/Upgrade Items Table */
122 $sql[] = 'CREATE TABLE ' . $this->entries . ' (
123 id BIGINT(20) NOT NULL auto_increment,
124 item_key varchar(100) default NULL,
125 name varchar(255) default NULL,
126 description text default NULL,
127 ip text default NULL,
128 form_id BIGINT(20) default NULL,
129 post_id BIGINT(20) default NULL,
130 user_id BIGINT(20) default NULL,
131 parent_item_id BIGINT(20) default 0,
132 is_draft tinyint(1) default 0,
133 updated_by BIGINT(20) default NULL,
134 created_at datetime NOT NULL,
135 updated_at datetime NOT NULL,
136 PRIMARY KEY (id),
137 KEY form_id (form_id),
138 KEY post_id (post_id),
139 KEY user_id (user_id),
140 KEY parent_item_id (parent_item_id),
141 UNIQUE KEY item_key (item_key)
142 )';
143
144 /* Create/Upgrade Meta Table */
145 $sql[] = 'CREATE TABLE ' . $this->entry_metas . ' (
146 id BIGINT(20) NOT NULL auto_increment,
147 meta_value longtext default NULL,
148 field_id BIGINT(20) NOT NULL,
149 item_id BIGINT(20) NOT NULL,
150 created_at datetime NOT NULL,
151 PRIMARY KEY (id),
152 KEY field_id (field_id),
153 KEY item_id (item_id)
154 )';
155
156 foreach ( $sql as $q ) {
157 if ( function_exists( 'dbDelta' ) ) {
158 dbDelta( $q . $charset_collate . ';' );
159 } else {
160 global $wpdb;
161 $wpdb->query( $q . $charset_collate ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
162 }
163 unset( $q );
164 }
165
166 $this->add_composite_indexes_for_entries();
167 }
168
169 /**
170 * These indexes help optimize database queries for entries.
171 *
172 * @since 6.6
173 * @since 6.16.3 idx_form_id_is_draft was also added to frm_items.
174 *
175 * @return void
176 */
177 private function add_composite_indexes_for_entries() {
178 global $wpdb;
179
180 $table_name = "{$wpdb->prefix}frm_items";
181 $index_name = 'idx_is_draft_created_at';
182
183 if ( ! self::index_exists( $table_name, $index_name ) ) {
184 $wpdb->query( "CREATE INDEX idx_is_draft_created_at ON `{$wpdb->prefix}frm_items` (is_draft, created_at)" );
185 }
186
187 $table_name = "{$wpdb->prefix}frm_item_metas";
188 $index_name = 'idx_field_id_item_id';
189
190 if ( ! self::index_exists( $table_name, $index_name ) ) {
191 $wpdb->query( "CREATE INDEX idx_field_id_item_id ON `{$wpdb->prefix}frm_item_metas` (field_id, item_id)" );
192 }
193
194 $table_name = "{$wpdb->prefix}frm_items";
195 $index_name = 'idx_form_id_is_draft';
196
197 if ( ! self::index_exists( $table_name, $index_name ) ) {
198 $wpdb->query( "CREATE INDEX idx_form_id_is_draft ON `{$wpdb->prefix}frm_items` (form_id, is_draft)" );
199 }
200 }
201
202 /**
203 * Check that an index exists in a database table before trying to add it (which results in an error).
204 *
205 * @since 6.6
206 *
207 * @param string $table_name
208 * @param string $index_name
209 * @return bool
210 */
211 private static function index_exists( $table_name, $index_name ) {
212 global $wpdb;
213 $row = $wpdb->get_row(
214 $wpdb->prepare(
215 'SELECT 1 FROM information_schema.statistics
216 WHERE table_schema = database()
217 AND table_name = %s
218 AND index_name = %s
219 LIMIT 1',
220 array( $table_name, $index_name )
221 )
222 );
223 return (bool) $row;
224 }
225
226 private function maybe_create_contact_form() {
227 $form_exists = FrmForm::get_id_by_key( 'contact-form' );
228 if ( ! $form_exists ) {
229 $this->add_default_template();
230 }
231 }
232
233 /**
234 * Create the default contact form
235 *
236 * @since 3.06
237 */
238 private function add_default_template() {
239 if ( FrmXMLHelper::check_if_libxml_disable_entity_loader_exists() ) {
240 // XML import is not enabled on your server.
241 return;
242 }
243
244 $set_err = libxml_use_internal_errors( true );
245 $loader = FrmXMLHelper::maybe_libxml_disable_entity_loader( true );
246
247 $file = FrmAppHelper::plugin_path() . '/classes/views/xml/default-templates.xml';
248 FrmXMLHelper::import_xml( $file );
249
250 libxml_use_internal_errors( $set_err );
251 FrmXMLHelper::maybe_libxml_disable_entity_loader( $loader );
252 }
253
254 /**
255 * @param int|string $old_db_version
256 */
257 private function migrate_data( $old_db_version ) {
258 if ( ! $old_db_version ) {
259 $old_db_version = get_option( 'frm_db_version' );
260 }
261 if ( strpos( $old_db_version, '-' ) ) {
262 $last_upgrade = explode( '-', $old_db_version );
263 $old_db_version = (int) $last_upgrade[1];
264 }
265
266 if ( ! is_numeric( $old_db_version ) ) {
267 // bail if we don't know the previous version
268 return;
269 }
270
271 $migrations = array( 16, 11, 16, 17, 23, 25, 86, 90, 97, 98, 101 );
272 foreach ( $migrations as $migration ) {
273 if ( FrmAppHelper::$db_version >= $migration && $old_db_version < $migration ) {
274 $function_name = 'migrate_to_' . $migration;
275 $this->$function_name();
276 }
277 }
278 }
279
280 public function uninstall() {
281 if ( ! current_user_can( 'administrator' ) ) {
282 $frm_settings = FrmAppHelper::get_settings();
283 wp_die( esc_html( $frm_settings->admin_permission ) );
284 }
285
286 global $wpdb, $wp_roles;
287
288 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->fields ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
289 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->forms ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
290 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->entries ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
291 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->entry_metas ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
292
293 delete_option( 'frm_options' );
294 delete_option( 'frm_db_version' );
295 delete_option( 'frm_install_running' );
296 delete_option( 'frm_lite_settings_upgrade' );
297 delete_option( 'frm-usage-uuid' );
298 delete_option( 'frm_inbox' );
299 delete_option( 'frmpro_css' );
300 delete_option( FrmOnboardingWizardController::REDIRECT_STATUS_OPTION );
301 delete_option( FrmEmailSummaryHelper::$option_name );
302
303 // Delete roles.
304 $frm_roles = FrmAppHelper::frm_capabilities();
305 $roles = get_editable_roles();
306 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
307 foreach ( $roles as $role => $details ) {
308 $wp_roles->remove_cap( $role, $frm_role );
309 unset( $role, $details );
310 }
311 unset( $frm_role, $frm_role_description );
312 }
313 unset( $roles, $frm_roles );
314
315 // delete actions, views, and styles
316
317 // prevent the post deletion from triggering entries to be deleted
318 remove_action( 'before_delete_post', 'FrmProDisplaysController::before_delete_post' );
319 remove_action( 'deleted_post', 'FrmProEntriesController::delete_entry' );
320
321 $post_ids = $wpdb->get_col( $wpdb->prepare( 'SELECT ID FROM ' . $wpdb->posts . ' WHERE post_type in (%s, %s, %s)', FrmFormActionsController::$action_post_type, FrmStylesController::$post_type, 'frm_display' ) );
322 foreach ( $post_ids as $post_id ) {
323 // Delete's each post.
324 wp_delete_post( $post_id, true );
325 }
326 unset( $post_ids );
327
328 // delete transients
329 delete_transient( 'frmpro_css' );
330 delete_transient( 'frm_options' );
331 delete_transient( 'frmpro_options' );
332 delete_transient( FrmOnboardingWizardController::TRANSIENT_NAME );
333
334 $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE %s OR option_name LIKE %s', '_transient_timeout_frm_form_fields_%', '_transient_frm_form_fields_%' ) );
335
336 do_action( 'frm_after_uninstall' );
337
338 return true;
339 }
340
341 /**
342 * Disables summary email for multisite (not the main site) if recipient setting isn't changed.
343 *
344 * @since 6.8
345 */
346 private function migrate_to_101() {
347 if ( ! is_multisite() || get_main_site_id() === get_current_blog_id() ) {
348 return;
349 }
350
351 $frm_settings = FrmAppHelper::get_settings();
352 if ( empty( $frm_settings->summary_emails ) || '[admin_email]' !== $frm_settings->summary_emails_recipients ) {
353 // User changed it.
354 return;
355 }
356
357 $frm_settings->summary_emails = 0;
358 $frm_settings->store();
359 }
360
361 /**
362 * Clear frmpro_css transient.
363 *
364 * @since 4.10.02
365 */
366 private function migrate_to_98() {
367 delete_transient( 'frmpro_css' );
368 }
369
370 /**
371 * Move default_blank and clear_on_focus to placeholder.
372 *
373 * @since 4.0
374 */
375 private function migrate_to_97() {
376 $this->migrate_to_placeholder( 'clear_on_focus' );
377 $this->migrate_to_placeholder( 'default_blank' );
378 }
379
380 /**
381 * Move clear_on_focus or default_blank to placeholder.
382 *
383 * @since 4.0
384 */
385 private function migrate_to_placeholder( $type = 'clear_on_focus' ) {
386 $query = array(
387 'field_options like' => '"' . $type . '";s:1:"1";',
388 );
389
390 $fields = FrmDb::get_results( $this->fields, $query, 'id, default_value, field_options, options' );
391
392 foreach ( $fields as $field ) {
393 FrmAppHelper::unserialize_or_decode( $field->field_options );
394 FrmAppHelper::unserialize_or_decode( $field->options );
395 $update_values = FrmXMLHelper::migrate_field_placeholder( $field, $type );
396 if ( empty( $update_values ) ) {
397 continue;
398 }
399
400 FrmField::update( $field->id, $update_values );
401 unset( $field );
402 }
403 }
404
405 /**
406 * Delete unneeded default templates
407 *
408 * @since 3.06
409 */
410 private function migrate_to_90() {
411 $form = FrmForm::getOne( 'contact' );
412 if ( $form && $form->default_template == 1 ) {
413 FrmForm::destroy( 'contact' );
414 }
415 }
416
417 /**
418 * Reverse migration 17 -- Divide by 9
419 *
420 * @since 3.0.05
421 */
422 private function migrate_to_86() {
423
424 $fields = $this->get_fields_with_size();
425
426 foreach ( (array) $fields as $f ) {
427 FrmAppHelper::unserialize_or_decode( $f->field_options );
428 $size = $f->field_options['size'];
429 $this->maybe_convert_migrated_size( $size );
430
431 if ( $size === $f->field_options['size'] ) {
432 continue;
433 }
434
435 $f->field_options['size'] = $size;
436 FrmField::update( $f->id, array( 'field_options' => $f->field_options ) );
437 unset( $f );
438 }
439
440 // reverse the extra size changes in widgets
441 $widgets = get_option( 'widget_frm_show_form' );
442 if ( empty( $widgets ) ) {
443 return;
444 }
445
446 $this->revert_widget_field_size();
447 }
448
449 private function get_fields_with_size() {
450 $field_types = array(
451 'textarea',
452 'text',
453 'number',
454 'email',
455 'url',
456 'rte',
457 'date',
458 'phone',
459 'password',
460 'image',
461 'tag',
462 'file',
463 );
464
465 $query = array(
466 'type' => $field_types,
467 'field_options like' => 's:4:"size";',
468 'field_options not like' => 's:4:"size";s:0:',
469 );
470
471 return FrmDb::get_results( $this->fields, $query, 'id, field_options' );
472 }
473
474 /**
475 * Reverse the extra size changes in widgets
476 *
477 * @since 3.0.05
478 */
479 private function revert_widget_field_size() {
480 $widgets = get_option( 'widget_frm_show_form' );
481 if ( empty( $widgets ) ) {
482 return;
483 }
484
485 FrmAppHelper::unserialize_or_decode( $widgets );
486 foreach ( $widgets as $k => $widget ) {
487 if ( ! is_array( $widget ) || ! isset( $widget['size'] ) ) {
488 continue;
489 }
490
491 $this->maybe_convert_migrated_size( $widgets[ $k ]['size'] );
492 }
493 update_option( 'widget_frm_show_form', $widgets );
494 }
495
496 /**
497 * Divide by 9 to reverse the multiplication
498 *
499 * @since 3.0.05
500 */
501 private function maybe_convert_migrated_size( &$size ) {
502 $has_px_size = ! empty( $size ) && strpos( $size, 'px' );
503 if ( ! $has_px_size ) {
504 return;
505 }
506
507 $int_size = str_replace( 'px', '', $size );
508 if ( ! is_numeric( $int_size ) || (int) $int_size < 900 ) {
509 return;
510 }
511
512 $pixel_conversion = 9;
513
514 $size = round( (int) $int_size / $pixel_conversion );
515 }
516
517 /**
518 * Migrate old styling settings. If sites are using the old
519 * default 400px field width, switch it to 100%
520 *
521 * @since 2.0.4
522 */
523 private function migrate_to_25() {
524 // get the style that was created with the style migration
525 $frm_style = new FrmStyle();
526 $styles = $frm_style->get_all( 'post_date', 'ASC', 1 );
527 if ( empty( $styles ) ) {
528 return;
529 }
530
531 foreach ( $styles as $style ) {
532 if ( $style->post_content['field_width'] === '400px' ) {
533 $style->post_content['field_width'] = '100%';
534 $frm_style->save( (array) $style );
535
536 return;
537 }
538 }
539 }
540
541 /**
542 * Check if the parent_form_id columns exists.
543 * If not, try and add it again
544 *
545 * @since 2.0.2
546 */
547 private function migrate_to_23() {
548 global $wpdb;
549 $exists = $wpdb->get_row( 'SHOW COLUMNS FROM ' . $this->forms . ' LIKE "parent_form_id"' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
550 if ( empty( $exists ) ) {
551 $wpdb->query( 'ALTER TABLE ' . $this->forms . ' ADD parent_form_id int(11) default 0' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
552 }
553 }
554
555 /**
556 * Change field size from character to pixel -- Multiply by 9
557 */
558 private function migrate_to_17() {
559 $fields = $this->get_fields_with_size();
560
561 foreach ( $fields as $f ) {
562 FrmAppHelper::unserialize_or_decode( $f->field_options );
563 if ( empty( $f->field_options['size'] ) || ! is_numeric( $f->field_options['size'] ) ) {
564 continue;
565 }
566
567 $this->convert_character_to_px( $f->field_options['size'] );
568
569 FrmField::update( $f->id, array( 'field_options' => $f->field_options ) );
570 unset( $f );
571 }
572
573 $this->adjust_widget_size();
574 }
575
576 /**
577 * Change the characters in widgets to pixels
578 */
579 private function adjust_widget_size() {
580 $widgets = get_option( 'widget_frm_show_form' );
581 if ( empty( $widgets ) ) {
582 return;
583 }
584
585 FrmAppHelper::unserialize_or_decode( $widgets );
586 foreach ( $widgets as $k => $widget ) {
587 if ( ! is_array( $widget ) || ! isset( $widget['size'] ) ) {
588 continue;
589 }
590 $this->convert_character_to_px( $widgets[ $k ]['size'] );
591 }
592 update_option( 'widget_frm_show_form', $widgets );
593 }
594
595 private function convert_character_to_px( &$size ) {
596 $pixel_conversion = 9;
597
598 $size = round( $pixel_conversion * (int) $size );
599 $size .= 'px';
600 }
601
602 /**
603 * Migrate post and email notification settings into actions
604 */
605 private function migrate_to_16() {
606 $forms = FrmDb::get_results( $this->forms, array(), 'id, options, is_template, default_template' );
607
608 /**
609 * Old email settings format:
610 * email_to: Email or field id
611 * also_email_to: array of fields ids
612 * reply_to: Email, field id, 'custom'
613 * cust_reply_to: string
614 * reply_to_name: field id, 'custom'
615 * cust_reply_to_name: string
616 * plain_text: 0|1
617 * email_message: string or ''
618 * email_subject: string or ''
619 * inc_user_info: 0|1
620 * update_email: 0, 1, 2
621 *
622 * Old autoresponder settings format:
623 * auto_responder: 0|1
624 * ar_email_message: string or ''
625 * ar_email_to: field id
626 * ar_plain_text: 0|1
627 * ar_reply_to_name: string
628 * ar_reply_to: string
629 * ar_email_subject: string
630 * ar_update_email: 0, 1, 2
631 *
632 * New email settings:
633 * post_content: json settings
634 * post_title: form id
635 * post_excerpt: message
636 */
637 foreach ( $forms as $form ) {
638 if ( $form->is_template && $form->default_template ) {
639 // don't migrate the default templates since the email will be added anyway
640 continue;
641 }
642
643 // Format form options
644 $form_options = $form->options;
645 FrmAppHelper::unserialize_or_decode( $form_options );
646
647 // Migrate settings to actions
648 FrmXMLHelper::migrate_form_settings_to_actions( $form_options, $form->id );
649 }
650 }
651
652 private function migrate_to_11() {
653 global $wpdb;
654
655 $forms = FrmDb::get_results( $this->forms, array(), 'id, options' );
656
657 $sending = __( 'Sending', 'formidable' );
658 $img = FrmAppHelper::plugin_url() . '/images/ajax_loader.gif';
659 $old_default_html = <<<DEFAULT_HTML
660 <div class="frm_submit">
661 [if back_button]<input type="submit" value="[back_label]" name="frm_prev_page" formnovalidate="formnovalidate" [back_hook] />[/if back_button]
662 <input type="submit" value="[button_label]" [button_action] />
663 <img class="frm_ajax_loading" src="$img" alt="$sending" style="visibility:hidden;" />
664 </div>
665 DEFAULT_HTML;
666 unset( $sending, $img );
667
668 $new_default_html = FrmFormsHelper::get_default_html( 'submit' );
669 $draft_link = FrmFormsHelper::get_draft_link();
670 foreach ( $forms as $form ) {
671 FrmAppHelper::unserialize_or_decode( $form->options );
672 if ( empty( $form->options['submit_html'] ) ) {
673 continue;
674 }
675
676 if ( $form->options['submit_html'] != $new_default_html && $form->options['submit_html'] == $old_default_html ) {
677 $form->options['submit_html'] = $new_default_html;
678 $wpdb->update( $this->forms, array( 'options' => serialize( $form->options ) ), array( 'id' => $form->id ) );
679 } elseif ( ! strpos( $form->options['submit_html'], 'save_draft' ) ) {
680 $form->options['submit_html'] = preg_replace( '~\<\/div\>(?!.*\<\/div\>)~', $draft_link . "\r\n</div>", $form->options['submit_html'] );
681 $wpdb->update( $this->forms, array( 'options' => serialize( $form->options ) ), array( 'id' => $form->id ) );
682 }
683 unset( $form );
684 }
685 }
686 }
687