'ai_webmaster_operator', 'fields' => 'all', ]); foreach ($users as $user) { if ($user instanceof WP_User) { $user->set_role('aiwest_operator'); } } } protected static function migrate_options() { global $wpdb; $legacy_prefix = 'aiw_'; $like = $wpdb->esc_like($legacy_prefix) . '%'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $rows = $wpdb->get_results( $wpdb->prepare( "SELECT option_name, option_value, autoload FROM {$wpdb->options} WHERE option_name LIKE %s", $like ), ARRAY_A ); foreach ((array) $rows as $row) { $old_name = (string) ($row['option_name'] ?? ''); if (strpos($old_name, $legacy_prefix) !== 0) continue; $new_name = 'aiwest_' . substr($old_name, strlen($legacy_prefix)); if (get_option($new_name, null) !== null) continue; $autoload = !in_array((string) ($row['autoload'] ?? ''), ['no', 'off', 'auto-off'], true); add_option($new_name, maybe_unserialize($row['option_value'] ?? ''), '', $autoload); } } protected static function migrate_metadata() { global $wpdb; $post_like = $wpdb->esc_like('_aiw_') . '%'; $user_like = $wpdb->esc_like('aiw_') . '%'; // Copy only missing post-meta keys. Existing AIWEST data always wins. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query( $wpdb->prepare( "INSERT INTO {$wpdb->postmeta} (post_id, meta_key, meta_value) SELECT legacy.post_id, CONCAT('_aiwest_', SUBSTRING(legacy.meta_key, 6)), legacy.meta_value FROM {$wpdb->postmeta} AS legacy WHERE legacy.meta_key LIKE %s AND NOT EXISTS ( SELECT 1 FROM {$wpdb->postmeta} AS current WHERE current.post_id = legacy.post_id AND current.meta_key = CONCAT('_aiwest_', SUBSTRING(legacy.meta_key, 6)) )", $post_like ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query( $wpdb->prepare( "INSERT INTO {$wpdb->usermeta} (user_id, meta_key, meta_value) SELECT legacy.user_id, CONCAT('aiwest_', SUBSTRING(legacy.meta_key, 5)), legacy.meta_value FROM {$wpdb->usermeta} AS legacy WHERE legacy.meta_key LIKE %s AND NOT EXISTS ( SELECT 1 FROM {$wpdb->usermeta} AS current WHERE current.user_id = legacy.user_id AND current.meta_key = CONCAT('aiwest_', SUBSTRING(legacy.meta_key, 5)) )", $user_like ) ); } protected static function migrate_database_tables() { global $wpdb; $tables = ['memory', 'goals', 'tasks', 'actions', 'logs', 'chat_history', 'content_history']; foreach ($tables as $suffix) { $legacy = $wpdb->prefix . 'aiw_' . $suffix; $current = $wpdb->prefix . 'aiwest_' . $suffix; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $legacy_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($legacy))) === $legacy; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $current_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($current))) === $current; if (!$legacy_exists || !$current_exists) continue; // Both names are generated exclusively from $wpdb->prefix and a fixed allowlist. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $wpdb->query("INSERT IGNORE INTO `{$current}` SELECT * FROM `{$legacy}`"); } } protected static function migrate_registered_content_types() { global $wpdb; $post_types = [ 'aiw_directory_item' => 'aiwest_dir_item', 'aiwest_directory_item' => 'aiwest_dir_item', 'aiwest_directory_ite' => 'aiwest_dir_item', 'aiw_directory_lead' => 'aiwest_dir_lead', 'aiwest_directory_lead' => 'aiwest_dir_lead', 'aiwest_directory_lea' => 'aiwest_dir_lead', ]; foreach ($post_types as $legacy => $current) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->update($wpdb->posts, ['post_type' => $current], ['post_type' => $legacy], ['%s'], ['%s']); } $taxonomies = [ 'aiw_directory_category' => 'aiwest_directory_category', 'aiw_directory_region' => 'aiwest_directory_region', 'aiw_directory_service' => 'aiwest_directory_service', ]; foreach ($taxonomies as $legacy => $current) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->update($wpdb->term_taxonomy, ['taxonomy' => $current], ['taxonomy' => $legacy], ['%s'], ['%s']); } // Core template metadata keeps the old filename until it is migrated explicitly. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->update( $wpdb->postmeta, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- One-time migration of plugin-owned template metadata. ['meta_value' => 'aiwest-canvas.php'], // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key, WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- One-time migration of a specific legacy template value. ['meta_key' => '_wp_page_template', 'meta_value' => 'aiw-canvas.php'], ['%s'], ['%s', '%s'] ); } protected static function migrate_saved_content() { $posts = get_posts([ 'post_type' => 'any', 'post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids', 's' => 'aiw', 'no_found_rows' => true, ]); foreach ($posts as $post_id) { $content = (string) get_post_field('post_content', $post_id, 'raw'); if ($content === '' || (strpos($content, '[aiw_') === false && strpos($content, 'aiw-') === false)) continue; $updated = str_replace( ['[aiw_', '[/aiw_', 'aiw-'], ['[aiwest_', '[/aiwest_', 'aiwest-'], $content ); if ($updated === $content) continue; add_post_meta($post_id, '_aiwest_1612_legacy_content_backup', $content, true); wp_update_post([ 'ID' => (int) $post_id, 'post_content' => $updated, ]); } } } add_action('admin_init', ['AIWEST_Legacy_Migration', 'maybe_run'], 1);