Exemplo n.º 1
0
/**
 * Removes the passed id_topic's.
 * Permissions are NOT checked here because the function is used in a scheduled task
 *
 * @param int[]|int $topics The topics to remove (can be an id or an array of ids).
 * @param bool $decreasePostCount if true users' post count will be reduced
 * @param bool $ignoreRecycling if true topics are not moved to the recycle board (if it exists).
 */
function removeTopics($topics, $decreasePostCount = true, $ignoreRecycling = false)
{
    global $modSettings;
    $db = database();
    // Nothing to do?
    if (empty($topics)) {
        return;
    }
    // Only a single topic.
    if (is_numeric($topics)) {
        $topics = array($topics);
    }
    // Decrease the post counts for members.
    if ($decreasePostCount) {
        $requestMembers = $db->query('', '
			SELECT m.id_member, COUNT(*) AS posts
			FROM {db_prefix}messages AS m
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
			WHERE m.id_topic IN ({array_int:topics})
				AND m.icon != {string:recycled}
				AND b.count_posts = {int:do_count_posts}
				AND m.approved = {int:is_approved}
			GROUP BY m.id_member', array('do_count_posts' => 0, 'recycled' => 'recycled', 'topics' => $topics, 'is_approved' => 1));
        if ($db->num_rows($requestMembers) > 0) {
            while ($rowMembers = $db->fetch_assoc($requestMembers)) {
                updateMemberData($rowMembers['id_member'], array('posts' => 'posts - ' . $rowMembers['posts']));
            }
        }
        $db->free_result($requestMembers);
    }
    // Recycle topics that aren't in the recycle board...
    if (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 && !$ignoreRecycling) {
        $request = $db->query('', '
			SELECT id_topic, id_board, unapproved_posts, approved
			FROM {db_prefix}topics
			WHERE id_topic IN ({array_int:topics})
				AND id_board != {int:recycle_board}
			LIMIT ' . count($topics), array('recycle_board' => $modSettings['recycle_board'], 'topics' => $topics));
        if ($db->num_rows($request) > 0) {
            // Get topics that will be recycled.
            $recycleTopics = array();
            while ($row = $db->fetch_assoc($request)) {
                if (function_exists('apache_reset_timeout')) {
                    @apache_reset_timeout();
                }
                $recycleTopics[] = $row['id_topic'];
                // Set the id_previous_board for this topic - and make it not sticky.
                $db->query('', '
					UPDATE {db_prefix}topics
					SET id_previous_board = {int:id_previous_board}, is_sticky = {int:not_sticky}
					WHERE id_topic = {int:id_topic}', array('id_previous_board' => $row['id_board'], 'id_topic' => $row['id_topic'], 'not_sticky' => 0));
            }
            $db->free_result($request);
            // Mark recycled topics as recycled.
            $db->query('', '
				UPDATE {db_prefix}messages
				SET icon = {string:recycled}
				WHERE id_topic IN ({array_int:recycle_topics})', array('recycle_topics' => $recycleTopics, 'recycled' => 'recycled'));
            // Move the topics to the recycle board.
            require_once SUBSDIR . '/Topic.subs.php';
            moveTopics($recycleTopics, $modSettings['recycle_board']);
            // Close reports that are being recycled.
            require_once SUBSDIR . '/Moderation.subs.php';
            $db->query('', '
				UPDATE {db_prefix}log_reported
				SET closed = {int:is_closed}
				WHERE id_topic IN ({array_int:recycle_topics})', array('recycle_topics' => $recycleTopics, 'is_closed' => 1));
            updateSettings(array('last_mod_report_action' => time()));
            recountOpenReports();
            // Topics that were recycled don't need to be deleted, so subtract them.
            $topics = array_diff($topics, $recycleTopics);
        } else {
            $db->free_result($request);
        }
    }
    // Still topics left to delete?
    if (empty($topics)) {
        return;
    }
    $adjustBoards = array();
    // Find out how many posts we are deleting.
    $request = $db->query('', '
		SELECT id_board, approved, COUNT(*) AS num_topics, SUM(unapproved_posts) AS unapproved_posts,
			SUM(num_replies) AS num_replies
		FROM {db_prefix}topics
		WHERE id_topic IN ({array_int:topics})
		GROUP BY id_board, approved', array('topics' => $topics));
    while ($row = $db->fetch_assoc($request)) {
        if (!isset($adjustBoards[$row['id_board']]['num_posts'])) {
            cache_put_data('board-' . $row['id_board'], null, 120);
            $adjustBoards[$row['id_board']] = array('num_posts' => 0, 'num_topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0, 'id_board' => $row['id_board']);
        }
        // Posts = (num_replies + 1) for each approved topic.
        $adjustBoards[$row['id_board']]['num_posts'] += $row['num_replies'] + ($row['approved'] ? $row['num_topics'] : 0);
        $adjustBoards[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts'];
        // Add the topics to the right type.
        if ($row['approved']) {
            $adjustBoards[$row['id_board']]['num_topics'] += $row['num_topics'];
        } else {
            $adjustBoards[$row['id_board']]['unapproved_topics'] += $row['num_topics'];
        }
    }
    $db->free_result($request);
    // Decrease number of posts and topics for each board.
    foreach ($adjustBoards as $stats) {
        if (function_exists('apache_reset_timeout')) {
            @apache_reset_timeout();
        }
        $db->query('', '
			UPDATE {db_prefix}boards
			SET
				num_posts = CASE WHEN {int:num_posts} > num_posts THEN 0 ELSE num_posts - {int:num_posts} END,
				num_topics = CASE WHEN {int:num_topics} > num_topics THEN 0 ELSE num_topics - {int:num_topics} END,
				unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END,
				unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END
			WHERE id_board = {int:id_board}', array('id_board' => $stats['id_board'], 'num_posts' => $stats['num_posts'], 'num_topics' => $stats['num_topics'], 'unapproved_posts' => $stats['unapproved_posts'], 'unapproved_topics' => $stats['unapproved_topics']));
    }
    // Remove polls for these topics.
    $request = $db->query('', '
		SELECT id_poll
		FROM {db_prefix}topics
		WHERE id_topic IN ({array_int:topics})
			AND id_poll > {int:no_poll}
		LIMIT ' . count($topics), array('no_poll' => 0, 'topics' => $topics));
    $polls = array();
    while ($row = $db->fetch_assoc($request)) {
        $polls[] = $row['id_poll'];
    }
    $db->free_result($request);
    if (!empty($polls)) {
        $db->query('', '
			DELETE FROM {db_prefix}polls
			WHERE id_poll IN ({array_int:polls})', array('polls' => $polls));
        $db->query('', '
			DELETE FROM {db_prefix}poll_choices
			WHERE id_poll IN ({array_int:polls})', array('polls' => $polls));
        $db->query('', '
			DELETE FROM {db_prefix}log_polls
			WHERE id_poll IN ({array_int:polls})', array('polls' => $polls));
    }
    // Get rid of the attachment(s).
    require_once SUBSDIR . '/ManageAttachments.subs.php';
    $attachmentQuery = array('attachment_type' => 0, 'id_topic' => $topics);
    removeAttachments($attachmentQuery, 'messages');
    // Delete search index entries.
    if (!empty($modSettings['search_custom_index_config'])) {
        $customIndexSettings = unserialize($modSettings['search_custom_index_config']);
        $request = $db->query('', '
			SELECT id_msg, body
			FROM {db_prefix}messages
			WHERE id_topic IN ({array_int:topics})', array('topics' => $topics));
        $words = array();
        $messages = array();
        while ($row = $db->fetch_assoc($request)) {
            if (function_exists('apache_reset_timeout')) {
                @apache_reset_timeout();
            }
            $words = array_merge($words, text2words($row['body'], $customIndexSettings['bytes_per_word'], true));
            $messages[] = $row['id_msg'];
        }
        $db->free_result($request);
        $words = array_unique($words);
        if (!empty($words) && !empty($messages)) {
            $db->query('', '
				DELETE FROM {db_prefix}log_search_words
				WHERE id_word IN ({array_int:word_list})
					AND id_msg IN ({array_int:message_list})', array('word_list' => $words, 'message_list' => $messages));
        }
    }
    // Reuse the message array if available
    if (empty($messages)) {
        $messages = messagesInTopics($topics);
    }
    // If there are messages left in this topic
    if (!empty($messages)) {
        // Decrease / Update the member like counts
        require_once SUBSDIR . '/Likes.subs.php';
        decreaseLikeCounts($messages);
        // Remove all likes now that the topic is gone
        $db->query('', '
			DELETE FROM {db_prefix}message_likes
			WHERE id_msg IN ({array_int:messages})', array('messages' => $messages));
        // Remove all mentions now that the topic is gone
        $db->query('', '
			DELETE FROM {db_prefix}log_mentions
			WHERE id_msg IN ({array_int:messages})', array('messages' => $messages));
    }
    // Delete messages in each topic.
    $db->query('', '
		DELETE FROM {db_prefix}messages
		WHERE id_topic IN ({array_int:topics})', array('topics' => $topics));
    // Remove linked calendar events.
    // @todo if unlinked events are enabled, wouldn't this be expected to keep them?
    $db->query('', '
		DELETE FROM {db_prefix}calendar
		WHERE id_topic IN ({array_int:topics})', array('topics' => $topics));
    // Delete log_topics data
    $db->query('', '
		DELETE FROM {db_prefix}log_topics
		WHERE id_topic IN ({array_int:topics})', array('topics' => $topics));
    // Delete notifications
    $db->query('', '
		DELETE FROM {db_prefix}log_notify
		WHERE id_topic IN ({array_int:topics})', array('topics' => $topics));
    // Delete the topics themselves
    $db->query('', '
		DELETE FROM {db_prefix}topics
		WHERE id_topic IN ({array_int:topics})', array('topics' => $topics));
    // Remove data from the subjects for search cache
    $db->query('', '
		DELETE FROM {db_prefix}log_search_subjects
		WHERE id_topic IN ({array_int:topics})', array('topics' => $topics));
    require_once SUBSDIR . '/FollowUps.subs.php';
    removeFollowUpsByTopic($topics);
    foreach ($topics as $topic_id) {
        cache_put_data('topic_board-' . $topic_id, null, 120);
    }
    // Maybe there's an addon that wants to delete topic related data of its own
    call_integration_hook('integrate_remove_topics', array($topics));
    // Update the totals...
    updateStats('message');
    updateTopicStats();
    updateSettings(array('calendar_updated' => time()));
    require_once SUBSDIR . '/Post.subs.php';
    $updates = array();
    foreach ($adjustBoards as $stats) {
        $updates[] = $stats['id_board'];
    }
    updateLastMessages($updates);
}
Exemplo n.º 2
0
/**
 * Remove a specific message.
 * !! This includes permission checks.
 *
 * - normally, local and global should be the localCookies and globalCookies settings, respectively.
 * - uses boardurl to determine these two things.
 *
 * @param int $message The message id
 * @param bool $decreasePostCount if true users' post count will be reduced
 */
function removeMessage($message, $decreasePostCount = true)
{
    global $board, $modSettings, $user_info;
    $db = database();
    if (empty($message) || !is_numeric($message)) {
        return false;
    }
    $request = $db->query('', '
		SELECT
			m.id_member, m.icon, m.poster_time, m.subject,' . (empty($modSettings['search_custom_index_config']) ? '' : ' m.body,') . '
			m.approved, t.id_topic, t.id_first_msg, t.id_last_msg, t.num_replies, t.id_board,
			t.id_member_started AS id_member_poster,
			b.count_posts
		FROM {db_prefix}messages AS m
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
		WHERE m.id_msg = {int:id_msg}
		LIMIT 1', array('id_msg' => $message));
    if ($db->num_rows($request) == 0) {
        return false;
    }
    $row = $db->fetch_assoc($request);
    $db->free_result($request);
    if (empty($board) || $row['id_board'] != $board) {
        $delete_any = boardsAllowedTo('delete_any');
        if (!in_array(0, $delete_any) && !in_array($row['id_board'], $delete_any)) {
            $delete_own = boardsAllowedTo('delete_own');
            $delete_own = in_array(0, $delete_own) || in_array($row['id_board'], $delete_own);
            $delete_replies = boardsAllowedTo('delete_replies');
            $delete_replies = in_array(0, $delete_replies) || in_array($row['id_board'], $delete_replies);
            if ($row['id_member'] == $user_info['id']) {
                if (!$delete_own) {
                    if ($row['id_member_poster'] == $user_info['id']) {
                        if (!$delete_replies) {
                            fatal_lang_error('cannot_delete_replies', 'permission');
                        }
                    } else {
                        fatal_lang_error('cannot_delete_own', 'permission');
                    }
                } elseif (($row['id_member_poster'] != $user_info['id'] || !$delete_replies) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time()) {
                    fatal_lang_error('modify_post_time_passed', false);
                }
            } elseif ($row['id_member_poster'] == $user_info['id']) {
                if (!$delete_replies) {
                    fatal_lang_error('cannot_delete_replies', 'permission');
                }
            } else {
                fatal_lang_error('cannot_delete_any', 'permission');
            }
        }
        // Can't delete an unapproved message, if you can't see it!
        if ($modSettings['postmod_active'] && !$row['approved'] && $row['id_member'] != $user_info['id'] && !(in_array(0, $delete_any) || in_array($row['id_board'], $delete_any))) {
            $approve_posts = !empty($user_info['mod_cache']['ap']) ? $user_info['mod_cache']['ap'] : boardsAllowedTo('approve_posts');
            if (!in_array(0, $approve_posts) && !in_array($row['id_board'], $approve_posts)) {
                return false;
            }
        }
    } else {
        // Check permissions to delete this message.
        if ($row['id_member'] == $user_info['id']) {
            if (!allowedTo('delete_own')) {
                if ($row['id_member_poster'] == $user_info['id'] && !allowedTo('delete_any')) {
                    isAllowedTo('delete_replies');
                } elseif (!allowedTo('delete_any')) {
                    isAllowedTo('delete_own');
                }
            } elseif (!allowedTo('delete_any') && ($row['id_member_poster'] != $user_info['id'] || !allowedTo('delete_replies')) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + $modSettings['edit_disable_time'] * 60 < time()) {
                fatal_lang_error('modify_post_time_passed', false);
            }
        } elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('delete_any')) {
            isAllowedTo('delete_replies');
        } else {
            isAllowedTo('delete_any');
        }
        if ($modSettings['postmod_active'] && !$row['approved'] && $row['id_member'] != $user_info['id'] && !allowedTo('delete_own')) {
            isAllowedTo('approve_posts');
        }
    }
    // Delete the *whole* topic, but only if the topic consists of one message.
    if ($row['id_first_msg'] == $message) {
        if (empty($board) || $row['id_board'] != $board) {
            $remove_any = boardsAllowedTo('remove_any');
            $remove_any = in_array(0, $remove_any) || in_array($row['id_board'], $remove_any);
            if (!$remove_any) {
                $remove_own = boardsAllowedTo('remove_own');
                $remove_own = in_array(0, $remove_own) || in_array($row['id_board'], $remove_own);
            }
            if ($row['id_member'] != $user_info['id'] && !$remove_any) {
                fatal_lang_error('cannot_remove_any', 'permission');
            } elseif (!$remove_any && !$remove_own) {
                fatal_lang_error('cannot_remove_own', 'permission');
            }
        } else {
            // Check permissions to delete a whole topic.
            if ($row['id_member'] != $user_info['id']) {
                isAllowedTo('remove_any');
            } elseif (!allowedTo('remove_any')) {
                isAllowedTo('remove_own');
            }
        }
        // ...if there is only one post.
        if (!empty($row['num_replies'])) {
            fatal_lang_error('delFirstPost', false);
        }
        // This needs to be included for topic functions
        require_once SUBSDIR . '/Topic.subs.php';
        removeTopics($row['id_topic']);
        return true;
    }
    // Deleting a recycled message can not lower anyone's post count.
    if ($row['icon'] == 'recycled') {
        $decreasePostCount = false;
    }
    // This is the last post, update the last post on the board.
    if ($row['id_last_msg'] == $message) {
        // Find the last message, set it, and decrease the post count.
        $request = $db->query('', '
			SELECT id_msg, id_member
			FROM {db_prefix}messages
			WHERE id_topic = {int:id_topic}
				AND id_msg != {int:id_msg}
			ORDER BY ' . ($modSettings['postmod_active'] ? 'approved DESC, ' : '') . 'id_msg DESC
			LIMIT 1', array('id_topic' => $row['id_topic'], 'id_msg' => $message));
        $row2 = $db->fetch_assoc($request);
        $db->free_result($request);
        $db->query('', '
			UPDATE {db_prefix}topics
			SET
				id_last_msg = {int:id_last_msg},
				id_member_updated = {int:id_member_updated}' . (!$modSettings['postmod_active'] || $row['approved'] ? ',
				num_replies = CASE WHEN num_replies = {int:no_replies} THEN 0 ELSE num_replies - 1 END' : ',
				unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . '
			WHERE id_topic = {int:id_topic}', array('id_last_msg' => $row2['id_msg'], 'id_member_updated' => $row2['id_member'], 'no_replies' => 0, 'no_unapproved' => 0, 'id_topic' => $row['id_topic']));
    } else {
        $db->query('', '
			UPDATE {db_prefix}topics
			SET ' . ($row['approved'] ? '
				num_replies = CASE WHEN num_replies = {int:no_replies} THEN 0 ELSE num_replies - 1 END' : '
				unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . '
			WHERE id_topic = {int:id_topic}', array('no_replies' => 0, 'no_unapproved' => 0, 'id_topic' => $row['id_topic']));
    }
    // Default recycle to false.
    $recycle = false;
    // If recycle topics has been set, make a copy of this message in the recycle board.
    // Make sure we're not recycling messages that are already on the recycle board.
    if (!empty($modSettings['recycle_enable']) && $row['id_board'] != $modSettings['recycle_board'] && $row['icon'] != 'recycled') {
        // Check if the recycle board exists and if so get the read status.
        $request = $db->query('', '
			SELECT (IFNULL(lb.id_msg, 0) >= b.id_msg_updated) AS is_seen, id_last_msg
			FROM {db_prefix}boards AS b
				LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = b.id_board AND lb.id_member = {int:current_member})
			WHERE b.id_board = {int:recycle_board}', array('current_member' => $user_info['id'], 'recycle_board' => $modSettings['recycle_board']));
        if ($db->num_rows($request) == 0) {
            fatal_lang_error('recycle_no_valid_board');
        }
        list($isRead, $last_board_msg) = $db->fetch_row($request);
        $db->free_result($request);
        // Is there an existing topic in the recycle board to group this post with?
        $request = $db->query('', '
			SELECT id_topic, id_first_msg, id_last_msg
			FROM {db_prefix}topics
			WHERE id_previous_topic = {int:id_previous_topic}
				AND id_board = {int:recycle_board}', array('id_previous_topic' => $row['id_topic'], 'recycle_board' => $modSettings['recycle_board']));
        list($id_recycle_topic, $first_topic_msg, $last_topic_msg) = $db->fetch_row($request);
        $db->free_result($request);
        // Insert a new topic in the recycle board if $id_recycle_topic is empty.
        if (empty($id_recycle_topic)) {
            $db->insert('', '{db_prefix}topics', array('id_board' => 'int', 'id_member_started' => 'int', 'id_member_updated' => 'int', 'id_first_msg' => 'int', 'id_last_msg' => 'int', 'unapproved_posts' => 'int', 'approved' => 'int', 'id_previous_topic' => 'int'), array($modSettings['recycle_board'], $row['id_member'], $row['id_member'], $message, $message, 0, 1, $row['id_topic']), array('id_topic'));
        }
        // Capture the ID of the new topic...
        $topicID = empty($id_recycle_topic) ? $db->insert_id('{db_prefix}topics', 'id_topic') : $id_recycle_topic;
        // If the topic creation went successful, move the message.
        if ($topicID > 0) {
            $db->query('', '
				UPDATE {db_prefix}messages
				SET
					id_topic = {int:id_topic},
					id_board = {int:recycle_board},
					icon = {string:recycled},
					approved = {int:is_approved}
				WHERE id_msg = {int:id_msg}', array('id_topic' => $topicID, 'recycle_board' => $modSettings['recycle_board'], 'id_msg' => $message, 'recycled' => 'recycled', 'is_approved' => 1));
            // Take any reported posts with us...
            $db->query('', '
				UPDATE {db_prefix}log_reported
				SET
					id_topic = {int:id_topic},
					id_board = {int:recycle_board}
				WHERE id_msg = {int:id_msg}', array('id_topic' => $topicID, 'recycle_board' => $modSettings['recycle_board'], 'id_msg' => $message));
            // Mark recycled topic as read.
            if (!$user_info['is_guest']) {
                require_once SUBSDIR . '/Topic.subs.php';
                markTopicsRead(array($user_info['id'], $topicID, $modSettings['maxMsgID'], 0), true);
            }
            // Mark recycle board as seen, if it was marked as seen before.
            if (!empty($isRead) && !$user_info['is_guest']) {
                require_once SUBSDIR . '/Boards.subs.php';
                markBoardsRead($modSettings['recycle_board']);
            }
            // Add one topic and post to the recycle bin board.
            $db->query('', '
				UPDATE {db_prefix}boards
				SET
					num_topics = num_topics + {int:num_topics_inc},
					num_posts = num_posts + 1' . ($message > $last_board_msg ? ', id_last_msg = {int:id_merged_msg}' : '') . '
				WHERE id_board = {int:recycle_board}', array('num_topics_inc' => empty($id_recycle_topic) ? 1 : 0, 'recycle_board' => $modSettings['recycle_board'], 'id_merged_msg' => $message));
            // Lets increase the num_replies, and the first/last message ID as appropriate.
            if (!empty($id_recycle_topic)) {
                $db->query('', '
					UPDATE {db_prefix}topics
					SET num_replies = num_replies + 1' . ($message > $last_topic_msg ? ', id_last_msg = {int:id_merged_msg}' : '') . ($message < $first_topic_msg ? ', id_first_msg = {int:id_merged_msg}' : '') . '
					WHERE id_topic = {int:id_recycle_topic}', array('id_recycle_topic' => $id_recycle_topic, 'id_merged_msg' => $message));
            }
            // Make sure this message isn't getting deleted later on.
            $recycle = true;
            // Make sure we update the search subject index.
            updateSubjectStats($topicID, $row['subject']);
        }
        // If it wasn't approved don't keep it in the queue.
        if (!$row['approved']) {
            $db->query('', '
				DELETE FROM {db_prefix}approval_queue
				WHERE id_msg = {int:id_msg}
					AND id_attach = {int:id_attach}', array('id_msg' => $message, 'id_attach' => 0));
        }
    }
    $db->query('', '
		UPDATE {db_prefix}boards
		SET ' . ($row['approved'] ? '
			num_posts = CASE WHEN num_posts = {int:no_posts} THEN 0 ELSE num_posts - 1 END' : '
			unapproved_posts = CASE WHEN unapproved_posts = {int:no_unapproved} THEN 0 ELSE unapproved_posts - 1 END') . '
		WHERE id_board = {int:id_board}', array('no_posts' => 0, 'no_unapproved' => 0, 'id_board' => $row['id_board']));
    // If the poster was registered and the board this message was on incremented
    // the member's posts when it was posted, decrease his or her post count.
    if (!empty($row['id_member']) && $decreasePostCount && empty($row['count_posts']) && $row['approved']) {
        updateMemberData($row['id_member'], array('posts' => '-'));
    }
    // Only remove posts if they're not recycled.
    if (!$recycle) {
        // Update the like counts
        require_once SUBSDIR . '/Likes.subs.php';
        decreaseLikeCounts($message);
        // Remove the likes!
        $db->query('', '
			DELETE FROM {db_prefix}message_likes
			WHERE id_msg = {int:id_msg}', array('id_msg' => $message));
        // Remove the mentions!
        $db->query('', '
			DELETE FROM {db_prefix}log_mentions
			WHERE id_msg = {int:id_msg}', array('id_msg' => $message));
        // Remove the message!
        $db->query('', '
			DELETE FROM {db_prefix}messages
			WHERE id_msg = {int:id_msg}', array('id_msg' => $message));
        if (!empty($modSettings['search_custom_index_config'])) {
            $customIndexSettings = unserialize($modSettings['search_custom_index_config']);
            $words = text2words($row['body'], $customIndexSettings['bytes_per_word'], true);
            if (!empty($words)) {
                $db->query('', '
					DELETE FROM {db_prefix}log_search_words
					WHERE id_word IN ({array_int:word_list})
						AND id_msg = {int:id_msg}', array('word_list' => $words, 'id_msg' => $message));
            }
        }
        // Delete attachment(s) if they exist.
        require_once SUBSDIR . '/ManageAttachments.subs.php';
        $attachmentQuery = array('attachment_type' => 0, 'id_msg' => $message);
        removeAttachments($attachmentQuery);
        // Delete follow-ups too
        require_once SUBSDIR . '/FollowUps.subs.php';
        // If it is an entire topic
        if ($row['id_first_msg'] == $message) {
            $db->query('', '
				DELETE FROM {db_prefix}follow_ups
				WHERE follow_ups IN ({array_int:topics})', array('topics' => $row['id_topic']));
        }
        // Allow mods to remove message related data of their own (likes, maybe?)
        call_integration_hook('integrate_remove_message', array($message));
    }
    // Update the pesky statistics.
    updateMessageStats();
    updateStats('topic');
    updateSettings(array('calendar_updated' => time()));
    // And now to update the last message of each board we messed with.
    require_once SUBSDIR . '/Post.subs.php';
    if ($recycle) {
        updateLastMessages(array($row['id_board'], $modSettings['recycle_board']));
    } else {
        updateLastMessages($row['id_board']);
    }
    // Close any moderation reports for this message.
    require_once SUBSDIR . '/Moderation.subs.php';
    $updated_reports = updateReportsStatus($message, 'close', 1);
    if ($updated_reports != 0) {
        updateSettings(array('last_mod_report_action' => time()));
        recountOpenReports();
    }
    return false;
}