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

679 lines 19.3 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 *
174 * @return void
175 */
176 private function add_composite_indexes_for_entries() {
177 global $wpdb;
178
179 $table_name = "{$wpdb->prefix}frm_items";
180 $index_name = 'idx_is_draft_created_at';
181
182 if ( ! self::index_exists( $table_name, $index_name ) ) {
183 $wpdb->query( "CREATE INDEX idx_is_draft_created_at ON `{$wpdb->prefix}frm_items` (is_draft, created_at)" );
184 }
185
186 $table_name = "{$wpdb->prefix}frm_item_metas";
187 $index_name = 'idx_field_id_item_id';
188
189 if ( ! self::index_exists( $table_name, $index_name ) ) {
190 $wpdb->query( "CREATE INDEX idx_field_id_item_id ON `{$wpdb->prefix}frm_item_metas` (field_id, item_id)" );
191 }
192 }
193
194 /**
195 * Check that an index exists in a database table before trying to add it (which results in an error).
196 *
197 * @since 6.6
198 *
199 * @param string $table_name
200 * @param string $index_name
201 * @return bool
202 */
203 private static function index_exists( $table_name, $index_name ) {
204 global $wpdb;
205 $row = $wpdb->get_row(
206 $wpdb->prepare(
207 'SELECT 1 FROM information_schema.statistics
208 WHERE table_schema = database()
209 AND table_name = %s
210 AND index_name = %s
211 LIMIT 1',
212 array( $table_name, $index_name )
213 )
214 );
215 return (bool) $row;
216 }
217
218 private function maybe_create_contact_form() {
219 $form_exists = FrmForm::get_id_by_key( 'contact-form' );
220 if ( ! $form_exists ) {
221 $this->add_default_template();
222 }
223 }
224
225 /**
226 * Create the default contact form
227 *
228 * @since 3.06
229 */
230 private function add_default_template() {
231 if ( FrmXMLHelper::check_if_libxml_disable_entity_loader_exists() ) {
232 // XML import is not enabled on your server.
233 return;
234 }
235
236 $set_err = libxml_use_internal_errors( true );
237 $loader = FrmXMLHelper::maybe_libxml_disable_entity_loader( true );
238
239 $file = FrmAppHelper::plugin_path() . '/classes/views/xml/default-templates.xml';
240 FrmXMLHelper::import_xml( $file );
241
242 libxml_use_internal_errors( $set_err );
243 FrmXMLHelper::maybe_libxml_disable_entity_loader( $loader );
244 }
245
246 /**
247 * @param int|string $old_db_version
248 */
249 private function migrate_data( $old_db_version ) {
250 if ( ! $old_db_version ) {
251 $old_db_version = get_option( 'frm_db_version' );
252 }
253 if ( strpos( $old_db_version, '-' ) ) {
254 $last_upgrade = explode( '-', $old_db_version );
255 $old_db_version = (int) $last_upgrade[1];
256 }
257
258 if ( ! is_numeric( $old_db_version ) ) {
259 // bail if we don't know the previous version
260 return;
261 }
262
263 $migrations = array( 16, 11, 16, 17, 23, 25, 86, 90, 97, 98, 101 );
264 foreach ( $migrations as $migration ) {
265 if ( FrmAppHelper::$db_version >= $migration && $old_db_version < $migration ) {
266 $function_name = 'migrate_to_' . $migration;
267 $this->$function_name();
268 }
269 }
270 }
271
272 public function uninstall() {
273 if ( ! current_user_can( 'administrator' ) ) {
274 $frm_settings = FrmAppHelper::get_settings();
275 wp_die( esc_html( $frm_settings->admin_permission ) );
276 }
277
278 global $wpdb, $wp_roles;
279
280 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->fields ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
281 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->forms ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
282 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->entries ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
283 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->entry_metas ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
284
285 delete_option( 'frm_options' );
286 delete_option( 'frm_db_version' );
287 delete_option( 'frm_install_running' );
288 delete_option( 'frm_lite_settings_upgrade' );
289 delete_option( 'frm-usage-uuid' );
290 delete_option( 'frm_inbox' );
291 delete_option( 'frmpro_css' );
292 delete_option( FrmOnboardingWizardController::REDIRECT_STATUS_OPTION );
293 delete_option( FrmEmailSummaryHelper::$option_name );
294
295 // Delete roles.
296 $frm_roles = FrmAppHelper::frm_capabilities();
297 $roles = get_editable_roles();
298 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
299 foreach ( $roles as $role => $details ) {
300 $wp_roles->remove_cap( $role, $frm_role );
301 unset( $role, $details );
302 }
303 unset( $frm_role, $frm_role_description );
304 }
305 unset( $roles, $frm_roles );
306
307 // delete actions, views, and styles
308
309 // prevent the post deletion from triggering entries to be deleted
310 remove_action( 'before_delete_post', 'FrmProDisplaysController::before_delete_post' );
311 remove_action( 'deleted_post', 'FrmProEntriesController::delete_entry' );
312
313 $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' ) );
314 foreach ( $post_ids as $post_id ) {
315 // Delete's each post.
316 wp_delete_post( $post_id, true );
317 }
318 unset( $post_ids );
319
320 // delete transients
321 delete_transient( 'frmpro_css' );
322 delete_transient( 'frm_options' );
323 delete_transient( 'frmpro_options' );
324 delete_transient( FrmOnboardingWizardController::TRANSIENT_NAME );
325
326 $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_%' ) );
327
328 do_action( 'frm_after_uninstall' );
329
330 return true;
331 }
332
333 /**
334 * Disables summary email for multisite (not the main site) if recipient setting isn't changed.
335 *
336 * @since 6.8
337 */
338 private function migrate_to_101() {
339 if ( ! is_multisite() || get_main_site_id() === get_current_blog_id() ) {
340 return;
341 }
342
343 $frm_settings = FrmAppHelper::get_settings();
344 if ( empty( $frm_settings->summary_emails ) || '[admin_email]' !== $frm_settings->summary_emails_recipients ) {
345 // User changed it.
346 return;
347 }
348
349 $frm_settings->summary_emails = 0;
350 $frm_settings->store();
351 }
352
353 /**
354 * Clear frmpro_css transient.
355 *
356 * @since 4.10.02
357 */
358 private function migrate_to_98() {
359 delete_transient( 'frmpro_css' );
360 }
361
362 /**
363 * Move default_blank and clear_on_focus to placeholder.
364 *
365 * @since 4.0
366 */
367 private function migrate_to_97() {
368 $this->migrate_to_placeholder( 'clear_on_focus' );
369 $this->migrate_to_placeholder( 'default_blank' );
370 }
371
372 /**
373 * Move clear_on_focus or default_blank to placeholder.
374 *
375 * @since 4.0
376 */
377 private function migrate_to_placeholder( $type = 'clear_on_focus' ) {
378 $query = array(
379 'field_options like' => '"' . $type . '";s:1:"1";',
380 );
381
382 $fields = FrmDb::get_results( $this->fields, $query, 'id, default_value, field_options, options' );
383
384 foreach ( $fields as $field ) {
385 FrmAppHelper::unserialize_or_decode( $field->field_options );
386 FrmAppHelper::unserialize_or_decode( $field->options );
387 $update_values = FrmXMLHelper::migrate_field_placeholder( $field, $type );
388 if ( empty( $update_values ) ) {
389 continue;
390 }
391
392 FrmField::update( $field->id, $update_values );
393 unset( $field );
394 }
395 }
396
397 /**
398 * Delete unneeded default templates
399 *
400 * @since 3.06
401 */
402 private function migrate_to_90() {
403 $form = FrmForm::getOne( 'contact' );
404 if ( $form && $form->default_template == 1 ) {
405 FrmForm::destroy( 'contact' );
406 }
407 }
408
409 /**
410 * Reverse migration 17 -- Divide by 9
411 *
412 * @since 3.0.05
413 */
414 private function migrate_to_86() {
415
416 $fields = $this->get_fields_with_size();
417
418 foreach ( (array) $fields as $f ) {
419 FrmAppHelper::unserialize_or_decode( $f->field_options );
420 $size = $f->field_options['size'];
421 $this->maybe_convert_migrated_size( $size );
422
423 if ( $size === $f->field_options['size'] ) {
424 continue;
425 }
426
427 $f->field_options['size'] = $size;
428 FrmField::update( $f->id, array( 'field_options' => $f->field_options ) );
429 unset( $f );
430 }
431
432 // reverse the extra size changes in widgets
433 $widgets = get_option( 'widget_frm_show_form' );
434 if ( empty( $widgets ) ) {
435 return;
436 }
437
438 $this->revert_widget_field_size();
439 }
440
441 private function get_fields_with_size() {
442 $field_types = array(
443 'textarea',
444 'text',
445 'number',
446 'email',
447 'url',
448 'rte',
449 'date',
450 'phone',
451 'password',
452 'image',
453 'tag',
454 'file',
455 );
456
457 $query = array(
458 'type' => $field_types,
459 'field_options like' => 's:4:"size";',
460 'field_options not like' => 's:4:"size";s:0:',
461 );
462
463 return FrmDb::get_results( $this->fields, $query, 'id, field_options' );
464 }
465
466 /**
467 * Reverse the extra size changes in widgets
468 *
469 * @since 3.0.05
470 */
471 private function revert_widget_field_size() {
472 $widgets = get_option( 'widget_frm_show_form' );
473 if ( empty( $widgets ) ) {
474 return;
475 }
476
477 FrmAppHelper::unserialize_or_decode( $widgets );
478 foreach ( $widgets as $k => $widget ) {
479 if ( ! is_array( $widget ) || ! isset( $widget['size'] ) ) {
480 continue;
481 }
482
483 $this->maybe_convert_migrated_size( $widgets[ $k ]['size'] );
484 }
485 update_option( 'widget_frm_show_form', $widgets );
486 }
487
488 /**
489 * Divide by 9 to reverse the multiplication
490 *
491 * @since 3.0.05
492 */
493 private function maybe_convert_migrated_size( &$size ) {
494 $has_px_size = ! empty( $size ) && strpos( $size, 'px' );
495 if ( ! $has_px_size ) {
496 return;
497 }
498
499 $int_size = str_replace( 'px', '', $size );
500 if ( ! is_numeric( $int_size ) || (int) $int_size < 900 ) {
501 return;
502 }
503
504 $pixel_conversion = 9;
505
506 $size = round( (int) $int_size / $pixel_conversion );
507 }
508
509 /**
510 * Migrate old styling settings. If sites are using the old
511 * default 400px field width, switch it to 100%
512 *
513 * @since 2.0.4
514 */
515 private function migrate_to_25() {
516 // get the style that was created with the style migration
517 $frm_style = new FrmStyle();
518 $styles = $frm_style->get_all( 'post_date', 'ASC', 1 );
519 if ( empty( $styles ) ) {
520 return;
521 }
522
523 foreach ( $styles as $style ) {
524 if ( $style->post_content['field_width'] === '400px' ) {
525 $style->post_content['field_width'] = '100%';
526 $frm_style->save( (array) $style );
527
528 return;
529 }
530 }
531 }
532
533 /**
534 * Check if the parent_form_id columns exists.
535 * If not, try and add it again
536 *
537 * @since 2.0.2
538 */
539 private function migrate_to_23() {
540 global $wpdb;
541 $exists = $wpdb->get_row( 'SHOW COLUMNS FROM ' . $this->forms . ' LIKE "parent_form_id"' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
542 if ( empty( $exists ) ) {
543 $wpdb->query( 'ALTER TABLE ' . $this->forms . ' ADD parent_form_id int(11) default 0' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
544 }
545 }
546
547 /**
548 * Change field size from character to pixel -- Multiply by 9
549 */
550 private function migrate_to_17() {
551 $fields = $this->get_fields_with_size();
552
553 foreach ( $fields as $f ) {
554 FrmAppHelper::unserialize_or_decode( $f->field_options );
555 if ( empty( $f->field_options['size'] ) || ! is_numeric( $f->field_options['size'] ) ) {
556 continue;
557 }
558
559 $this->convert_character_to_px( $f->field_options['size'] );
560
561 FrmField::update( $f->id, array( 'field_options' => $f->field_options ) );
562 unset( $f );
563 }
564
565 $this->adjust_widget_size();
566 }
567
568 /**
569 * Change the characters in widgets to pixels
570 */
571 private function adjust_widget_size() {
572 $widgets = get_option( 'widget_frm_show_form' );
573 if ( empty( $widgets ) ) {
574 return;
575 }
576
577 FrmAppHelper::unserialize_or_decode( $widgets );
578 foreach ( $widgets as $k => $widget ) {
579 if ( ! is_array( $widget ) || ! isset( $widget['size'] ) ) {
580 continue;
581 }
582 $this->convert_character_to_px( $widgets[ $k ]['size'] );
583 }
584 update_option( 'widget_frm_show_form', $widgets );
585 }
586
587 private function convert_character_to_px( &$size ) {
588 $pixel_conversion = 9;
589
590 $size = round( $pixel_conversion * (int) $size );
591 $size .= 'px';
592 }
593
594 /**
595 * Migrate post and email notification settings into actions
596 */
597 private function migrate_to_16() {
598 $forms = FrmDb::get_results( $this->forms, array(), 'id, options, is_template, default_template' );
599
600 /**
601 * Old email settings format:
602 * email_to: Email or field id
603 * also_email_to: array of fields ids
604 * reply_to: Email, field id, 'custom'
605 * cust_reply_to: string
606 * reply_to_name: field id, 'custom'
607 * cust_reply_to_name: string
608 * plain_text: 0|1
609 * email_message: string or ''
610 * email_subject: string or ''
611 * inc_user_info: 0|1
612 * update_email: 0, 1, 2
613 *
614 * Old autoresponder settings format:
615 * auto_responder: 0|1
616 * ar_email_message: string or ''
617 * ar_email_to: field id
618 * ar_plain_text: 0|1
619 * ar_reply_to_name: string
620 * ar_reply_to: string
621 * ar_email_subject: string
622 * ar_update_email: 0, 1, 2
623 *
624 * New email settings:
625 * post_content: json settings
626 * post_title: form id
627 * post_excerpt: message
628 */
629 foreach ( $forms as $form ) {
630 if ( $form->is_template && $form->default_template ) {
631 // don't migrate the default templates since the email will be added anyway
632 continue;
633 }
634
635 // Format form options
636 $form_options = $form->options;
637 FrmAppHelper::unserialize_or_decode( $form_options );
638
639 // Migrate settings to actions
640 FrmXMLHelper::migrate_form_settings_to_actions( $form_options, $form->id );
641 }
642 }
643
644 private function migrate_to_11() {
645 global $wpdb;
646
647 $forms = FrmDb::get_results( $this->forms, array(), 'id, options' );
648
649 $sending = __( 'Sending', 'formidable' );
650 $img = FrmAppHelper::plugin_url() . '/images/ajax_loader.gif';
651 $old_default_html = <<<DEFAULT_HTML
652 <div class="frm_submit">
653 [if back_button]<input type="submit" value="[back_label]" name="frm_prev_page" formnovalidate="formnovalidate" [back_hook] />[/if back_button]
654 <input type="submit" value="[button_label]" [button_action] />
655 <img class="frm_ajax_loading" src="$img" alt="$sending" style="visibility:hidden;" />
656 </div>
657 DEFAULT_HTML;
658 unset( $sending, $img );
659
660 $new_default_html = FrmFormsHelper::get_default_html( 'submit' );
661 $draft_link = FrmFormsHelper::get_draft_link();
662 foreach ( $forms as $form ) {
663 FrmAppHelper::unserialize_or_decode( $form->options );
664 if ( empty( $form->options['submit_html'] ) ) {
665 continue;
666 }
667
668 if ( $form->options['submit_html'] != $new_default_html && $form->options['submit_html'] == $old_default_html ) {
669 $form->options['submit_html'] = $new_default_html;
670 $wpdb->update( $this->forms, array( 'options' => serialize( $form->options ) ), array( 'id' => $form->id ) );
671 } elseif ( ! strpos( $form->options['submit_html'], 'save_draft' ) ) {
672 $form->options['submit_html'] = preg_replace( '~\<\/div\>(?!.*\<\/div\>)~', $draft_link . "\r\n</div>", $form->options['submit_html'] );
673 $wpdb->update( $this->forms, array( 'options' => serialize( $form->options ) ), array( 'id' => $form->id ) );
674 }
675 unset( $form );
676 }
677 }
678 }
679