esc_like( $wpdb->prefix . 'esm_' ) . '%'; $table = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $like ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery if ( $table ) { return true; } return (bool) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM %i WHERE option_name LIKE %s', $wpdb->options, $wpdb->esc_like( 'esm_' ) . '%' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery } /** Called early on every load and on activation. */ public static function maybe_run() { if ( self::old_plugin_active() || ! self::has_legacy_data() ) { return; } self::run(); } public static function run() { global $wpdb; $report = array(); // 1. Tables: wp_esm_* -> wp_seatt_*. $like = $wpdb->esc_like( $wpdb->prefix . 'esm_' ) . '%'; $tables = (array) $wpdb->get_col( $wpdb->prepare( 'SHOW TABLES LIKE %s', $like ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery foreach ( $tables as $old ) { $new = $wpdb->prefix . 'seatt_' . substr( $old, strlen( $wpdb->prefix . 'esm_' ) ); $exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $new ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery if ( $exists ) { $rows = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM %i', $new ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery if ( $rows > 0 ) { $report[] = 'kept ' . $new; continue; } $wpdb->query( $wpdb->prepare( 'DROP TABLE %i', $new ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.SchemaChange } $wpdb->query( $wpdb->prepare( 'RENAME TABLE %i TO %i', $old, $new ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.SchemaChange $report[] = 'table ' . $new; } // 2. Settings: esm_* -> seatt_* (a setting already saved under the // new name wins). Old transients are simply dropped. $wpdb->query( $wpdb->prepare( 'DELETE FROM %i WHERE option_name LIKE %s OR option_name LIKE %s', $wpdb->options, $wpdb->esc_like( '_transient_esm_' ) . '%', $wpdb->esc_like( '_transient_timeout_esm_' ) . '%' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery $opts = (array) $wpdb->get_col( $wpdb->prepare( 'SELECT option_name FROM %i WHERE option_name LIKE %s', $wpdb->options, $wpdb->esc_like( 'esm_' ) . '%' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery foreach ( $opts as $name ) { $new = 'seatt_' . substr( $name, 4 ); $has = $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM %i WHERE option_name = %s', $wpdb->options, $new ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery if ( $has ) { $wpdb->delete( $wpdb->options, array( 'option_name' => $name ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery } else { $wpdb->update( $wpdb->options, array( 'option_name' => $new ), array( 'option_name' => $name ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery } } $report[] = count( $opts ) . ' settings'; // 3. Events: post type and their saved details. $wpdb->update( $wpdb->posts, array( 'post_type' => 'seatt_event' ), array( 'post_type' => 'esm_event' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery $wpdb->query( $wpdb->prepare( 'UPDATE %i SET meta_key = CONCAT( \'_seatt_\', SUBSTRING( meta_key, 6 ) ) WHERE meta_key LIKE %s', $wpdb->postmeta, $wpdb->esc_like( '_esm_' ) . '%' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery $wpdb->query( $wpdb->prepare( 'UPDATE %i SET meta_key = CONCAT( \'seatt_\', SUBSTRING( meta_key, 5 ) ) WHERE meta_key LIKE %s', $wpdb->postmeta, $wpdb->esc_like( 'esm_' ) . '%' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery $wpdb->query( $wpdb->prepare( 'UPDATE %i SET meta_key = CONCAT( \'seatt_\', SUBSTRING( meta_key, 5 ) ) WHERE meta_key LIKE %s', $wpdb->usermeta, $wpdb->esc_like( 'esm_' ) . '%' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery // 4. Shortcodes in pages: [esm_booking ...] -> [seatt_booking ...]. $wpdb->query( $wpdb->prepare( 'UPDATE %i SET post_content = REPLACE( REPLACE( post_content, \'[esm_\', \'[seatt_\' ), \'[/esm_\', \'[/seatt_\' ) WHERE post_content LIKE %s OR post_content LIKE %s', $wpdb->posts, '%' . $wpdb->esc_like( '[esm_' ) . '%', '%' . $wpdb->esc_like( '[/esm_' ) . '%' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery // Page builders keep their own copy of the content in post meta // (JSON or PHP-serialized). Serialized values are rewritten through // unserialize/serialize so their string lengths stay valid. $metas = (array) $wpdb->get_results( $wpdb->prepare( 'SELECT meta_id, meta_value FROM %i WHERE meta_value LIKE %s', $wpdb->postmeta, '%' . $wpdb->esc_like( '[esm_' ) . '%' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery foreach ( $metas as $m ) { $value = (string) $m->meta_value; if ( is_serialized( $value ) ) { $data = maybe_unserialize( $value ); if ( ! is_object( $data ) ) { $value = maybe_serialize( self::replace_deep( $data ) ); } } else { $value = self::replace_shortcodes( $value ); } $wpdb->update( $wpdb->postmeta, array( 'meta_value' => $value ), array( 'meta_id' => (int) $m->meta_id ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.SlowDBQuery.slow_db_query_meta_value } // 5. Roles and capabilities. $roles_key = $wpdb->prefix . 'user_roles'; $roles = get_option( $roles_key ); if ( is_array( $roles ) ) { $out = array(); foreach ( $roles as $role => $def ) { $role = self::rename_key( $role ); if ( isset( $def['capabilities'] ) && is_array( $def['capabilities'] ) ) { $caps = array(); foreach ( $def['capabilities'] as $cap => $grant ) { $caps[ self::rename_key( $cap ) ] = $grant; } $def['capabilities'] = $caps; } $out[ $role ] = $def; } update_option( $roles_key, $out ); } $cap_key = $wpdb->prefix . 'capabilities'; $users = (array) $wpdb->get_results( $wpdb->prepare( 'SELECT umeta_id, meta_value FROM %i WHERE meta_key = %s AND meta_value LIKE %s', $wpdb->usermeta, $cap_key, '%' . $wpdb->esc_like( 'esm_' ) . '%' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery foreach ( $users as $u ) { $caps = maybe_unserialize( $u->meta_value ); if ( is_array( $caps ) ) { $new = array(); foreach ( $caps as $cap => $grant ) { $new[ self::rename_key( $cap ) ] = $grant; } $wpdb->update( $wpdb->usermeta, array( 'meta_value' => maybe_serialize( $new ) ), array( 'umeta_id' => (int) $u->umeta_id ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery } } // 6. Old scheduled jobs (new ones are scheduled under the new names). $cron = _get_cron_array(); if ( is_array( $cron ) ) { foreach ( $cron as $hooks ) { foreach ( array_keys( (array) $hooks ) as $hook ) { if ( 0 === strpos( (string) $hook, 'esm_' ) ) { wp_clear_scheduled_hook( $hook ); } } } } wp_cache_flush(); update_option( self::DONE, array( 'at' => time(), 'report' => $report ), false ); // Rebuild anything newer than the moved data (new columns etc.). delete_option( 'seatt_db_version' ); return $report; } private static function rename_key( $key ) { return 0 === strpos( (string) $key, 'esm_' ) ? 'seatt_' . substr( (string) $key, 4 ) : $key; } private static function replace_shortcodes( $text ) { return str_replace( array( '[esm_', '[/esm_' ), array( '[seatt_', '[/seatt_' ), (string) $text ); } private static function replace_deep( $data ) { if ( is_array( $data ) ) { foreach ( $data as $k => $v ) { $data[ $k ] = self::replace_deep( $v ); } return $data; } return is_string( $data ) ? self::replace_shortcodes( $data ) : $data; } public static function notice() { if ( ! current_user_can( 'activate_plugins' ) || ! self::old_plugin_active() || get_option( self::DONE ) ) { return; } echo '

' . esc_html__( 'SeatHive', 'seathive' ) . ': ' . esc_html__( 'the older SeatWP plugin is still active. Deactivate it (Plugins screen) — your events, bookings and settings then move over to SeatHive automatically.', 'seathive' ) . '

'; } public static function handle_now() { if ( ! current_user_can( 'activate_plugins' ) ) { wp_die( esc_html__( 'Not allowed.', 'seathive' ) ); } check_admin_referer( 'seatt_migrate_now' ); self::maybe_run(); wp_safe_redirect( admin_url( 'edit.php?post_type=seatt_event' ) ); exit; } }