diff --git a/phpBB/includes/functions_privmsgs.php b/phpBB/includes/functions_privmsgs.php index 447920cfd5..d2fce000aa 100644 --- a/phpBB/includes/functions_privmsgs.php +++ b/phpBB/includes/functions_privmsgs.php @@ -1083,6 +1083,166 @@ function delete_pm($user_id, $msg_ids, $folder_id) return true; } +/** +* Delete all PM(s) for a given user and delete the ones without references +* +* @param int $user_id ID of the user whose private messages we want to delete +* +* @return boolean False if there were no pms found, true otherwise. +*/ +function phpbb_delete_user_pms($user_id) +{ + global $db, $user, $phpbb_root_path, $phpEx; + + $user_id = (int) $user_id; + + if (!$user_id) + { + return false; + } + + // Get PM Information for later deleting + // The two queries where split, so we can use our indexes + // Part 1: get PMs the user received + $sql = 'SELECT msg_id, author_id, folder_id, pm_unread, pm_new + FROM ' . PRIVMSGS_TO_TABLE . ' + WHERE user_id = ' . $user_id; + $result = $db->sql_query($sql); + + $undelivered_msg = $undelivered_user = $delete_ids = array(); + while ($row = $db->sql_fetchrow($result)) + { + if ($row['author_id'] == $user_id && $row['folder_id'] == PRIVMSGS_NO_BOX) + { + // Undelivered messages + $undelivered_msg[] = $row['msg_id']; + + if (isset($undelivered_user[$row['user_id']])) + { + ++$undelivered_user[$row['user_id']]; + } + else + { + $undelivered_user[$row['user_id']] = 1; + } + } + + $delete_ids[(int) $row['msg_id']] = (int) $row['msg_id']; + } + $db->sql_freeresult($result); + + // Part 2: get PMs the user sent + $sql = 'SELECT msg_id, author_id, folder_id, pm_unread, pm_new + FROM ' . PRIVMSGS_TO_TABLE . ' + WHERE author_id = ' . $user_id . ' + AND folder_id = ' . PRIVMSGS_NO_BOX; + $result = $db->sql_query($sql); + + while ($row = $db->sql_fetchrow($result)) + { + if ($row['author_id'] == $user_id && $row['folder_id'] == PRIVMSGS_NO_BOX) + { + // Undelivered messages + $undelivered_msg[] = $row['msg_id']; + + if (isset($undelivered_user[$row['user_id']])) + { + ++$undelivered_user[$row['user_id']]; + } + else + { + $undelivered_user[$row['user_id']] = 1; + } + } + + $delete_ids[(int) $row['msg_id']] = (int) $row['msg_id']; + } + $db->sql_freeresult($result); + + if (empty($delete_ids)) + { + return false; + } + + $db->sql_transaction('begin'); + + if (sizeof($undelivered_msg)) + { + $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . ' + WHERE ' . $db->sql_in_set('msg_id', $undelivered_msg); + $db->sql_query($sql); + } + + // Reset the userīs pm count to 0 + if (isset($undelivered_user[$user_id])) + { + $sql = 'UPDATE ' . USERS_TABLE . ' + SET user_new_privmsg = 0, + user_unread_privmsg = 0 + WHERE user_id = ' . $user_id; + $db->sql_query($sql); + unset($undelivered_user[$user_id]); + } + + foreach ($undelivered_user as $_user_id => $count) + { + $sql = 'UPDATE ' . USERS_TABLE . ' + SET user_new_privmsg = user_new_privmsg - ' . $count . ', + user_unread_privmsg = user_unread_privmsg - ' . $count . ' + WHERE user_id = ' . $_user_id; + $db->sql_query($sql); + } + + // Delete private message data + $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . " + WHERE user_id = $user_id + AND " . $db->sql_in_set('msg_id', $delete_ids); + $db->sql_query($sql); + + // Now we have to check which messages we can delete completely + $sql = 'SELECT msg_id + FROM ' . PRIVMSGS_TO_TABLE . ' + WHERE ' . $db->sql_in_set('msg_id', $delete_ids); + $result = $db->sql_query($sql); + + while ($row = $db->sql_fetchrow($result)) + { + unset($delete_ids[$row['msg_id']]); + } + $db->sql_freeresult($result); + + if (!empty($delete_ids)) + { + // Check if there are any attachments we need to remove + if (!function_exists('delete_attachments')) + { + include($phpbb_root_path . 'includes/functions_admin.' . $phpEx); + } + + delete_attachments('message', $delete_ids, false); + + $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . ' + WHERE ' . $db->sql_in_set('msg_id', $delete_ids); + $db->sql_query($sql); + } + + // Set the remaining author id to anonymous + // This way users are still able to read messages from users being removed + $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . ' + SET author_id = ' . ANONYMOUS . ' + WHERE author_id = ' . $user_id; + $db->sql_query($sql); + + $sql = 'UPDATE ' . PRIVMSGS_TABLE . ' + SET author_id = ' . ANONYMOUS . ' + WHERE author_id = ' . $user_id; + $db->sql_query($sql); + + $db->sql_transaction('commit'); + + return true; +} + /** * Rebuild message header */ diff --git a/phpBB/includes/functions_user.php b/phpBB/includes/functions_user.php index 83316be2a3..5a6a0b4a05 100644 --- a/phpBB/includes/functions_user.php +++ b/phpBB/includes/functions_user.php @@ -528,62 +528,12 @@ function user_delete($mode, $user_id, $post_username = false) WHERE session_user_id = ' . $user_id; $db->sql_query($sql); - // Remove any undelivered mails... - $sql = 'SELECT msg_id, user_id - FROM ' . PRIVMSGS_TO_TABLE . ' - WHERE author_id = ' . $user_id . ' - AND folder_id = ' . PRIVMSGS_NO_BOX; - $result = $db->sql_query($sql); - - $undelivered_msg = $undelivered_user = array(); - while ($row = $db->sql_fetchrow($result)) + // Clean the private messages tables from the user + if (!function_exists('phpbb_delete_user_pms')) { - $undelivered_msg[] = $row['msg_id']; - $undelivered_user[$row['user_id']][] = true; - } - $db->sql_freeresult($result); - - if (sizeof($undelivered_msg)) - { - $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . ' - WHERE ' . $db->sql_in_set('msg_id', $undelivered_msg); - $db->sql_query($sql); - } - - $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . ' - WHERE author_id = ' . $user_id . ' - AND folder_id = ' . PRIVMSGS_NO_BOX; - $db->sql_query($sql); - - // Delete all to-information - $sql = 'DELETE FROM ' . PRIVMSGS_TO_TABLE . ' - WHERE user_id = ' . $user_id; - $db->sql_query($sql); - - // Set the remaining author id to anonymous - this way users are still able to read messages from users being removed - $sql = 'UPDATE ' . PRIVMSGS_TO_TABLE . ' - SET author_id = ' . ANONYMOUS . ' - WHERE author_id = ' . $user_id; - $db->sql_query($sql); - - $sql = 'UPDATE ' . PRIVMSGS_TABLE . ' - SET author_id = ' . ANONYMOUS . ' - WHERE author_id = ' . $user_id; - $db->sql_query($sql); - - foreach ($undelivered_user as $_user_id => $ary) - { - if ($_user_id == $user_id) - { - continue; - } - - $sql = 'UPDATE ' . USERS_TABLE . ' - SET user_new_privmsg = user_new_privmsg - ' . sizeof($ary) . ', - user_unread_privmsg = user_unread_privmsg - ' . sizeof($ary) . ' - WHERE user_id = ' . $_user_id; - $db->sql_query($sql); + include($phpbb_root_path . 'includes/functions_privmsgs.' . $phpEx); } + phpbb_delete_user_pms($user_id); $db->sql_transaction('commit'); diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index 6097341ace..c700b483a5 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -2026,7 +2026,7 @@ function change_database_data(&$no_updates, $version) // No changes from 3.0.10-RC3 to 3.0.10 case '3.0.10-RC3': break; - + // Changes from 3.0.10 to 3.0.11-RC1 case '3.0.10': // Updates users having current style a deactivated one @@ -2050,6 +2050,44 @@ function change_database_data(&$no_updates, $version) _sql($sql, $errored, $error_ary); } + // Delete orphan private messages + $batch_size = 500; + + $sql_array = array( + 'SELECT' => 'p.msg_id', + 'FROM' => array( + PRIVMSGS_TABLE => 'p', + ), + 'LEFT_JOIN' => array( + array( + 'FROM' => array(PRIVMSGS_TO_TABLE => 't'), + 'ON' => 'p.msg_id = t.msg_id', + ), + ), + 'WHERE' => 't.user_id IS NULL', + ); + $sql = $db->sql_build_query('SELECT', $sql_array); + + do + { + $result = $db->sql_query_limit($sql, $batch_size); + + $delete_pms = array(); + while ($row = $db->sql_fetchrow($result)) + { + $delete_pms[] = (int) $row['msg_id']; + } + $db->sql_freeresult($result); + + if (!empty($delete_pms)) + { + $sql = 'DELETE FROM ' . PRIVMSGS_TABLE . ' + WHERE ' . $db->sql_in_set('msg_id', $delete_pms); + _sql($sql, $errored, $error_ary); + } + } + while (sizeof($delete_pms) == $batch_size); + $no_updates = false; break; }