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

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