'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