'sitereply-chat-support/sitereply-chat-support.php', 'prefix' => 'sitereply_', 'role' => 'sitereply_agent', 'cap' => 'sitereply_live_chat', 'report' => 'sitereply_report', ), array( 'plugin' => 'chat-support/chat-support.php', 'prefix' => 'chat_support_', 'role' => 'chat_support_agent', 'cap' => 'chat_support_live_chat', 'report' => 'chat_support_report', ), array( 'plugin' => 'rhc-faq-chatbot/rhc-faq-chatbot.php', 'prefix' => 'rhc_faq_', 'role' => 'rhc_chat_agent', 'cap' => 'rhc_live_chat', 'report' => 'rhc_faq_report', ), ); } public static function hooks() { add_action( 'admin_init', array( __CLASS__, 'deactivate_old' ) ); add_action( 'admin_notices', array( __CLASS__, 'notice' ) ); } /** * Whether the plugin at this path really is an earlier build of this * plugin (not someone else's plugin that happens to use the same folder). */ private static function is_earlier_build( $plugin_file ) { $path = WP_PLUGIN_DIR . '/' . $plugin_file; if ( ! file_exists( $path ) || ! file_exists( dirname( $path ) . '/templates/faq-template.json' ) ) { return false; } $data = get_file_data( $path, array( 'domain' => 'Text Domain' ) ); return dirname( $plugin_file ) === $data['domain']; } /** Whether saved data with this prefix was written by an earlier build of this plugin. */ private static function has_earlier_data( $prefix ) { global $wpdb; $settings = get_option( $prefix . 'settings' ); if ( is_array( $settings ) && array_key_exists( 'popular_title', $settings ) && array_key_exists( 'bot_name', $settings ) ) { return true; } // Settings never saved: recognise the earlier build by its questions table. $table = $wpdb->prefix . $prefix . 'items'; if ( ! self::table_exists( $table ) ) { return false; } $columns = $wpdb->get_col( $wpdb->prepare( 'SHOW COLUMNS FROM %i', $table ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- One-time upgrade check. return ! array_diff( array( 'slug', 'question', 'answer', 'related', 'featured', 'source', 'clicks' ), (array) $columns ); } private static function table_exists( $name ) { global $wpdb; return $name === $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $name ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- One-time upgrade check. } private static function table_has_rows( $name ) { global $wpdb; return (int) $wpdb->get_var( $wpdb->prepare( 'SELECT COUNT(*) FROM %i', $name ) ) > 0; // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- One-time upgrade check. } public static function run() { global $wpdb; if ( get_option( 'replyvane_migrated' ) ) { return; } $moved = false; foreach ( self::legacy() as $old ) { if ( ! self::has_earlier_data( $old['prefix'] ) ) { continue; // Nothing from an earlier build of this plugin under this prefix. } // 1. Database tables: rename them, keeping every question, chat and page read. foreach ( self::TABLES as $suffix ) { $from = $wpdb->prefix . $old['prefix'] . $suffix; $to = $wpdb->prefix . 'replyvane_' . $suffix; if ( ! self::table_exists( $from ) ) { continue; } if ( self::table_exists( $to ) ) { if ( self::table_has_rows( $to ) ) { continue; // Already filled from a newer build: leave the older table alone. } $wpdb->query( $wpdb->prepare( 'DROP TABLE %i', $to ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.SchemaChange -- Empty table created before the upgrade. } $wpdb->query( $wpdb->prepare( 'RENAME TABLE %i TO %i', $from, $to ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.SchemaChange -- One-time rename of the plugin's own table. $moved = true; } // 2. Settings and other saved values (not the earlier builds' own upgrade markers). // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- One-time upgrade: list the earlier option names. $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT option_name FROM %i WHERE option_name LIKE %s', $wpdb->options, $wpdb->esc_like( $old['prefix'] ) . '%' ), ARRAY_A ); foreach ( (array) $rows as $row ) { $key = substr( $row['option_name'], strlen( $old['prefix'] ) ); if ( in_array( $key, array( 'migrated', 'old_deactivated' ), true ) ) { continue; } $new = 'replyvane_' . $key; if ( false === get_option( $new, false ) ) { update_option( $new, get_option( $row['option_name'] ), 'replyvane_settings' === $new ); $moved = true; } } // 3. Live Chat Agents keep their access. if ( get_role( $old['role'] ) ) { Replyvane_Live::setup_roles(); foreach ( get_users( array( 'role' => $old['role'], 'fields' => 'ID' ) ) as $uid ) { $user = new WP_User( $uid ); $user->add_role( Replyvane_Live::ROLE ); $user->remove_role( $old['role'] ); } remove_role( $old['role'] ); $moved = true; } $admin = get_role( 'administrator' ); if ( $admin && $admin->has_cap( $old['cap'] ) ) { $admin->remove_cap( $old['cap'] ); } // 4. Support reports sent from this site. $wpdb->update( $wpdb->posts, array( 'post_type' => Replyvane_Support::POST_TYPE ), array( 'post_type' => $old['report'] ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- One-time upgrade. // 5. Scheduled tasks of the earlier copy. foreach ( array( 'cleanup', 'kb_daily', 'kb_process', 'kb_single', 'live_escalate' ) as $hook ) { wp_unschedule_hook( $old['prefix'] . $hook ); } } update_option( 'replyvane_migrated', $moved ? 'moved' : 'fresh', false ); } /** * Switches off earlier copies so the chat button doesn't appear twice. * * @param bool $on_activation True when called while this plugin is being activated. */ public static function deactivate_old( $on_activation = false ) { if ( true !== $on_activation && ! current_user_can( 'activate_plugins' ) ) { return; } if ( ! function_exists( 'is_plugin_active' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } foreach ( self::legacy() as $old ) { if ( is_plugin_active( $old['plugin'] ) && self::is_earlier_build( $old['plugin'] ) ) { deactivate_plugins( $old['plugin'], true ); update_option( 'replyvane_old_deactivated', 1, false ); } } } public static function notice() { if ( ! current_user_can( 'activate_plugins' ) || ! get_option( 'replyvane_old_deactivated' ) ) { return; } $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen || ! in_array( $screen->id, array( 'plugins', 'toplevel_page_replyvane' ), true ) ) { return; } if ( ! function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $plugins = get_plugins(); $left = false; foreach ( self::legacy() as $old ) { if ( isset( $plugins[ $old['plugin'] ] ) && self::is_earlier_build( $old['plugin'] ) ) { $left = true; } } if ( ! $left ) { delete_option( 'replyvane_old_deactivated' ); return; } printf( '

%s %s

', esc_html__( 'Replyvane Chat Support is installed.', 'replyvane-chat-support' ), esc_html__( 'Your questions, settings, chats and team members were moved to this version, and the earlier copy of this plugin was switched off. You can now delete the earlier copy from the Plugins screen.', 'replyvane-chat-support' ) ); } }