Index: src/bp-messages/bp-messages-functions.php
===================================================================
--- src/bp-messages/bp-messages-functions.php
+++ src/bp-messages/bp-messages-functions.php
@@ -550,7 +550,33 @@
 	return $retval;
 }
 
-/** Email *********************************************************************/
+/** Hooks *********************************************************************/
+
+/**
+ * Delete all message threads that the deleted user has sent.
+ *
+ * @since 2.9.0
+ *
+ * @param int $user_id The deleted user ID.
+ */
+function bp_messages_delete_threads_on_user_delete( $user_id ) {
+	$threads = BP_Messages_Thread::get_current_threads_for_user( array(
+		'user_id'     => $user_id,
+		'box'         => 'sentbox',
+		'deleted'     => null,
+		'fields'      => 'ids',
+		'count_total' => false
+	) );
+
+	if ( empty( $threads ) ) {
+		return;
+	}
+
+	foreach ( $threads['threads'] as $thread_id ) {
+		BP_Messages_Thread::delete( $thread_id, $user_id, true );
+	}
+}
+add_action( 'deleted_user', 'bp_messages_delete_threads_on_user_delete' );
 
 /**
  * Email message recipients to alert them of a new unread private message.
Index: src/bp-messages/classes/class-bp-messages-thread.php
===================================================================
--- src/bp-messages/classes/class-bp-messages-thread.php
+++ src/bp-messages/classes/class-bp-messages-thread.php
@@ -328,18 +328,21 @@
 	 * @since 1.0.0
 	 * @since 2.7.0 The $user_id parameter was added. Previously the current user
 	 *              was always assumed.
-	 *
-	 * @param int $thread_id The message thread ID.
-	 * @param int $user_id The ID of the user in the thread to mark messages as
-	 *                     deleted for. Defaults to the current logged-in user.
-	 *
+	 * @since 2.9.0 Added $force as a parameter.
+	 *
+	 * @param int  $thread_id The message thread ID.
+	 * @param int  $user_id   The ID of the user in the thread to mark messages as deleted for. Defaults
+	 *                        to the current logged-in user.
+	 * @param bool $force     Whether to delete the entire message thread for all recipients. If false,
+	 *                        every recipient in a thread needs to mark the thread as deleted before the
+	 *                        thread is deleted from the DB. Default: false.
 	 * @return bool
 	 */
-	public static function delete( $thread_id = 0, $user_id = 0 ) {
+	public static function delete( $thread_id = 0, $user_id = 0, $force = false ) {
 		global $wpdb;
 
-		$thread_id = (int) $thread_id;
-		$user_id = (int) $user_id;
+		$thread_id  = (int) $thread_id;
+		$user_id    = (int) $user_id;
 
 		if ( empty( $user_id ) ) {
 			$user_id = bp_loggedin_user_id();
@@ -358,17 +361,20 @@
 
 		$bp = buddypress();
 
-		// Mark messages as deleted
-		$wpdb->query( $wpdb->prepare( "UPDATE {$bp->messages->table_name_recipients} SET is_deleted = 1 WHERE thread_id = %d AND user_id = %d", $thread_id, $user_id ) );
-
 		// Get the message ids in order to pass to the action.
 		$message_ids = $wpdb->get_col( $wpdb->prepare( "SELECT id FROM {$bp->messages->table_name_messages} WHERE thread_id = %d", $thread_id ) );
 
-		// Check to see if any more recipients remain for this message.
-		$recipients = $wpdb->get_results( $wpdb->prepare( "SELECT id FROM {$bp->messages->table_name_recipients} WHERE thread_id = %d AND is_deleted = 0", $thread_id ) );
+		// Do the following when a user manually deletes a thread.
+		if ( false === $force ) {
+			// Mark messages as deleted
+			$wpdb->query( $wpdb->prepare( "UPDATE {$bp->messages->table_name_recipients} SET is_deleted = 1 WHERE thread_id = %d AND user_id = %d", $thread_id, $user_id ) );
+
+			// Check to see if any more recipients remain for this message.
+			$recipients = $wpdb->get_results( $wpdb->prepare( "SELECT id FROM {$bp->messages->table_name_recipients} WHERE thread_id = %d AND is_deleted = 0", $thread_id ) );
+		}
 
 		// No more recipients so delete all messages associated with the thread.
-		if ( empty( $recipients ) ) {
+		if ( true === $force || ( false === $force && isset( $recipients ) && empty( $recipients ) ) ) {
 
 			/**
 			 * Fires before an entire message thread is deleted.
@@ -422,18 +428,26 @@
 	 * Get current message threads for a user.
 	 *
 	 * @since 1.0.0
+	 * @since 2.9.0 Added $deleted, $fields and $count_total as parameters to $args.
 	 *
 	 * @param array $args {
 	 *     Array of arguments.
-	 *     @type int    $user_id      The user ID.
-	 *     @type string $box          The type of mailbox to get. Either 'inbox' or 'sentbox'.
-	 *                                Defaults to 'inbox'.
-	 *     @type string $type         The type of messages to get. Either 'all' or 'unread'
-	 *                                or 'read'. Defaults to 'all'.
-	 *     @type int    $limit        The number of messages to get. Defaults to null.
-	 *     @type int    $page         The page number to get. Defaults to null.
-	 *     @type string $search_terms The search term to use. Defaults to ''.
-	 *     @type array  $meta_query   Meta query arguments. See WP_Meta_Query for more details.
+	 *     @type int      $user_id      The user ID.
+	 *     @type string   $box          The type of mailbox to get. Either 'inbox' or 'sentbox'.
+	 *                                  Defaults to 'inbox'.
+	 *     @type string   $type         The type of messages to get. Either 'all' or 'unread'
+	 *                                  or 'read'. Defaults to 'all'.
+	 *     @type int      $limit        The number of messages to get. Defaults to null.
+	 *     @type int      $page         The page number to get. Defaults to null.
+	 *     @type string   $search_terms The search term to use. Defaults to ''.
+	 *     @type array    $meta_query   Meta query arguments. See WP_Meta_Query for more details.
+	 *     @type int|null $deleted      Whether to fetch deleted messages. 0 means we will not fetch deleted
+	 *                                  messages; 1 means we will. null means we will not query for deleted
+	 *                                  messages. Default: 0.
+	 *     @type string   $fields       Thread fields to retrieve. 'all' to fetch entire thread objects, 'ids' to
+	 *                                  get only the message thread IDs. DefaultL 'all'.
+	 *     @type bool     $count_total  If true, an additional DB query is run to count the total message threads
+	 *                                  for the query. Default: true.
 	 * }
 	 * @return array|bool Array on success. Boolean false on failure.
 	 */
@@ -463,93 +477,121 @@
 			'limit'        => null,
 			'page'         => null,
 			'search_terms' => '',
-			'meta_query'   => array()
+			'meta_query'   => array(),
+			'deleted'      => 0,
+			'fields'       => 'all',
+			'count_total'  => true
 		) );
 
-		$pag_sql = $type_sql = $search_sql = $user_id_sql = $sender_sql = '';
-		$meta_query_sql = array(
-			'join'  => '',
-			'where' => ''
-		);
+		$pag_sql = $meta_query_join_sql = '';
+		$where   = array();
 
 		if ( $r['limit'] && $r['page'] ) {
 			$pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $r['page'] - 1 ) * $r['limit'] ), intval( $r['limit'] ) );
 		}
 
 		if ( $r['type'] == 'unread' ) {
-			$type_sql = " AND r.unread_count != 0 ";
+			$where['type'] = "r.unread_count != 0 ";
 		} elseif ( $r['type'] == 'read' ) {
-			$type_sql = " AND r.unread_count = 0 ";
+			$where['type'] = "r.unread_count = 0 ";
 		}
 
 		if ( ! empty( $r['search_terms'] ) ) {
 			$search_terms_like = '%' . bp_esc_like( $r['search_terms'] ) . '%';
-			$search_sql        = $wpdb->prepare( "AND ( subject LIKE %s OR message LIKE %s )", $search_terms_like, $search_terms_like );
+			$where['search']   = $wpdb->prepare( "( subject LIKE %s OR message LIKE %s )", $search_terms_like, $search_terms_like );
 		}
 
 		$r['user_id'] = (int) $r['user_id'];
 
-		// Default deleted SQL.
-		$deleted_sql = 'r.is_deleted = 0';
+		// Deleted SQL.
+		if ( is_int( $r['deleted'] ) ) {
+			$where['delete'] = $wpdb->prepare( "r.is_deleted = %d", $r['deleted'] );
+		}
 
 		switch ( $r['box'] ) {
 			case 'sentbox' :
-				$user_id_sql = 'AND ' . $wpdb->prepare( 'm.sender_id = %d', $r['user_id'] );
-				$sender_sql  = 'AND m.sender_id = r.user_id';
+				$where['user_id'] = $wpdb->prepare( 'm.sender_id = %d', $r['user_id'] );
+				$where['sender']  = 'm.sender_id = r.user_id';
 				break;
 
 			case 'inbox' :
-				$user_id_sql = 'AND ' . $wpdb->prepare( 'r.user_id = %d', $r['user_id'] );
-				$sender_sql  = 'AND r.sender_only = 0';
+				$where['user_id'] = $wpdb->prepare( 'r.user_id = %d', $r['user_id'] );
+				$where['sender']  = 'r.sender_only = 0';
 				break;
 
 			default :
 				// Omit user-deleted threads from all other custom message boxes.
-				$deleted_sql = $wpdb->prepare( '( r.user_id = %d AND r.is_deleted = 0 )', $r['user_id'] );
+				$where['custom_box'] = $wpdb->prepare( '( r.user_id = %d AND r.is_deleted = 0 )', $r['user_id'] );
 				break;
 		}
 
 		// Process meta query into SQL.
 		$meta_query = self::get_meta_query_sql( $r['meta_query'] );
 		if ( ! empty( $meta_query['join'] ) ) {
-			$meta_query_sql['join'] = $meta_query['join'];
+			$meta_query_join_sql = $meta_query['join'];
 		}
 		if ( ! empty( $meta_query['where'] ) ) {
-			$meta_query_sql['where'] = $meta_query['where'];
+			// Strips leading AND; also see BP_Groups_Group::strip_leading_and().
+			$where['meta_query'] = preg_replace( '/^\s*AND\s*/', '', $meta_query['where'] );
 		}
 
 		$bp = buddypress();
 
 		// Set up SQL array.
 		$sql = array();
-		$sql['select'] = 'SELECT m.thread_id, MAX(m.date_sent) AS date_sent';
-		$sql['from']   = "FROM {$bp->messages->table_name_recipients} r INNER JOIN {$bp->messages->table_name_messages} m ON m.thread_id = r.thread_id {$meta_query_sql['join']}";
-		$sql['where']  = "WHERE {$deleted_sql} {$user_id_sql} {$sender_sql} {$type_sql} {$search_sql} {$meta_query_sql['where']}";
-		$sql['misc']   = "GROUP BY m.thread_id ORDER BY date_sent DESC {$pag_sql}";
+		if ( 'ids' === $r['fields'] ) {
+			$sql['select'] = 'SELECT m.thread_id';
+		} else {
+			$sql['select'] = 'SELECT m.thread_id, MAX(m.date_sent) AS date_sent';
+		}
+		$sql['from']  = "FROM {$bp->messages->table_name_recipients} r INNER JOIN {$bp->messages->table_name_messages} m ON m.thread_id = r.thread_id {$meta_query_join_sql}";
+		$sql['where'] = 'WHERE ' . join( ' AND ', $where );
+		$sql['misc']  = "GROUP BY m.thread_id ORDER BY date_sent DESC {$pag_sql}";
 
 		// Get thread IDs.
-		$thread_ids = $wpdb->get_results( implode( ' ', $sql ) );
+		if ( 'ids' === $r['fields'] ) {
+			$thread_ids = $wpdb->get_col( implode( ' ', $sql ) );
+		} else {
+			$thread_ids = $wpdb->get_results( implode( ' ', $sql ) );
+		}
 		if ( empty( $thread_ids ) ) {
 			return false;
 		}
 
 		// Adjust $sql to work for thread total.
-		$sql['select'] = 'SELECT COUNT( DISTINCT m.thread_id )';
-		unset( $sql['misc'] );
-		$total_threads = $wpdb->get_var( implode( ' ', $sql ) );
+		$total_threads = null;
+		if ( true === $r['count_total'] ) {
+			$sql['select'] = 'SELECT COUNT( DISTINCT m.thread_id )';
+			unset( $sql['misc'] );
+			$total_threads = $wpdb->get_var( implode( ' ', $sql ) );
+		}
+
+		if ( 'all' === $r['fields'] ) {
+			// Sort threads by date_sent.
+			foreach( (array) $thread_ids as $thread ) {
+				$sorted_threads[ $thread->thread_id ] = strtotime( $thread->date_sent );
+			}
 
-		// Sort threads by date_sent.
-		foreach( (array) $thread_ids as $thread ) {
-			$sorted_threads[ $thread->thread_id ] = strtotime( $thread->date_sent );
+			arsort( $sorted_threads );
+
+			$threads = array();
+			foreach ( (array) $sorted_threads as $thread_id => $date_sent ) {
+				$threads[] = new BP_Messages_Thread( $thread_id, 'ASC', array(
+					'update_meta_cache' => false
+				) );
+			}
 		}
 
-		arsort( $sorted_threads );
+		// Set up return value.
+		if ( 'all' === $r['fields'] ) {
+			$threads = &$threads;
+		} else {
+			$threads = $thread_ids;
+		}
+		$retval = array( 'threads' => $threads );
 
-		$threads = array();
-		foreach ( (array) $sorted_threads as $thread_id => $date_sent ) {
-			$threads[] = new BP_Messages_Thread( $thread_id, 'ASC', array(
-				'update_meta_cache' => false
-			) );
+		if ( null !== $total_threads ) {
+			$retval['total'] = (int) $total_threads;
 		}
 
 		/**
@@ -558,14 +600,12 @@
 		 * @since 2.2.0
 		 *
 		 * @param array $value {
-		 *     @type array $threads       Array of threads. Passed by reference.
-		 *     @type int   $total_threads Number of threads found by the query.
+		 *     @type array $threads       Array of threads. Passed by reference if 'fields' is 'all' in the query.
+		 *     @type int   $total_threads Number of threads found by the query. This is added as an array key if
+		 *                                'count_total' is true in the query.
 		 * }
 		 */
-		return apply_filters( 'bp_messages_thread_current_threads', array(
-			'threads' => &$threads,
-			'total'   => (int) $total_threads
-		) );
+		return apply_filters( 'bp_messages_thread_current_threads', $retval );
 	}
 
 	/**
Index: tests/phpunit/testcases/messages/template.php
===================================================================
--- tests/phpunit/testcases/messages/template.php
+++ tests/phpunit/testcases/messages/template.php
@@ -359,4 +359,39 @@
 
 		$_REQUEST = $request;
 	}
+
+	/**
+	 * @group delete
+	 * @group BP_Messages_Box_Template
+	 */
+	public function test_bp_messages_template_should_not_have_threads_by_deleted_user() {
+		$u1 = $this->factory->user->create();
+		$u2 = $this->factory->user->create();
+		$u3 = $this->factory->user->create();
+
+		// create initial thread
+		$this->factory->message->create( array(
+			'sender_id'  => $u2,
+			'recipients' => array( $u1 ),
+		) );
+
+		// create some threads by our deleted user
+		$this->factory->message->create_many( 3, array(
+			'sender_id'  => $u3,
+			'recipients' => array( $u1 ),
+		) );
+
+		// Delete user 3.
+		if ( is_multisite() ) {
+			wpmu_delete_user( $u3 );
+		} else {
+			wp_delete_user( $u3 );
+		}
+
+		// Only the initial thread should remain.
+		$threads = new BP_Messages_Box_Template( array(
+			'user_id' => $u1
+		) );
+		$this->assertEquals( 1, $threads->thread_count );
+	}
 }
