add_action( 'after_uninstall', array( __CLASS__, 'run' ) ); } else { register_uninstall_hook( KDOCS_FILE, array( __CLASS__, 'run' ) ); } } /** * Uninstall callback (single site or every site of a network). */ public static function run() { if ( is_multisite() ) { foreach ( get_sites( array( 'fields' => 'ids', 'number' => 0, ) ) as $site_id ) { switch_to_blog( $site_id ); self::cleanup_site(); restore_current_blog(); } return; } self::cleanup_site(); } /** * Whether the site owner opted in to data removal. * * @return bool */ private static function opted_in() { $settings = get_option( 'kdocs_settings', array() ); return is_array( $settings ) && ! empty( $settings['delete_data'] ); } /** * Cleans the current site. */ public static function cleanup_site() { global $wpdb; // Scheduled events are always removed: they would fail without the plugin. foreach ( array( 'kdocs_weekly_digest' ) as $hook ) { Jobs::cancel( $hook ); } if ( ! self::opted_in() ) { return; } // phpcs:disable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared // Articles (incl. revisions / autosaves), their meta and comments. $ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s", 'kdocs_article' ) ); if ( $ids ) { $ids = array_map( 'intval', $ids ); $in = esc_sql( implode( ',', $ids ) ); $wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE post_id IN ( SELECT ID FROM {$wpdb->posts} WHERE post_parent IN ({$in}) AND post_type = 'revision' )" ); $wpdb->query( "DELETE FROM {$wpdb->posts} WHERE post_parent IN ({$in}) AND post_type = 'revision'" ); $wpdb->query( "DELETE FROM {$wpdb->postmeta} WHERE post_id IN ({$in})" ); $wpdb->query( "DELETE FROM {$wpdb->commentmeta} WHERE comment_id IN ( SELECT comment_ID FROM {$wpdb->comments} WHERE comment_post_ID IN ({$in}) )" ); $wpdb->query( "DELETE FROM {$wpdb->comments} WHERE comment_post_ID IN ({$in})" ); $wpdb->query( "DELETE FROM {$wpdb->term_relationships} WHERE object_id IN ({$in})" ); $wpdb->query( "DELETE FROM {$wpdb->posts} WHERE ID IN ({$in})" ); } // Knowledge bases, categories, versions and their meta. foreach ( array( 'kdocs_kb', 'kdocs_category', 'kdocs_version' ) as $taxonomy ) { $term_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_id FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s", $taxonomy ) ); if ( $term_ids ) { $in = esc_sql( implode( ',', array_map( 'intval', $term_ids ) ) ); $wpdb->query( "DELETE FROM {$wpdb->termmeta} WHERE term_id IN ({$in})" ); $wpdb->query( "DELETE tr FROM {$wpdb->term_relationships} tr INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id WHERE tt.taxonomy = '" . esc_sql( $taxonomy ) . "'" ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->term_taxonomy} WHERE taxonomy = %s", $taxonomy ) ); $wpdb->query( "DELETE FROM {$wpdb->terms} WHERE term_id IN ({$in}) AND term_id NOT IN ( SELECT term_id FROM {$wpdb->term_taxonomy} )" ); } } // Plugin tables (core and Pro). foreach ( Install::tables() as $table ) { $wpdb->query( $wpdb->prepare( 'DROP TABLE IF EXISTS %i', $wpdb->prefix . 'kdocs_' . $table ) ); } // Options, transients and user meta. $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE 'kdocs\_%' OR option_name LIKE '\_transient\_kdocs\_%' OR option_name LIKE '\_transient\_timeout\_kdocs\_%'" ); $wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE 'kdocs\_%'" ); // phpcs:enable Capabilities::uninstall(); // Workflow roles (Pro); users holding them fall back to no role. foreach ( array( 'docs_contributor', 'docs_editor' ) as $role ) { remove_role( $role ); } wp_cache_flush(); flush_rewrite_rules( false ); } }