Exemplo n.º 1
0
/**
 * Approve (or not) some posts... without permission checks...
 *
 * @package Posts
 * @param int[] $msgs - array of message ids
 * @param bool $approve = true
 */
function approvePosts($msgs, $approve = true)
{
    global $modSettings;
    $db = database();
    if (!is_array($msgs)) {
        $msgs = array($msgs);
    }
    if (empty($msgs)) {
        return false;
    }
    // May as well start at the beginning, working out *what* we need to change.
    $request = $db->query('', '
		SELECT m.id_msg, m.approved, m.id_topic, m.id_board, t.id_first_msg, t.id_last_msg,
			m.body, m.subject, IFNULL(mem.real_name, m.poster_name) AS poster_name, m.id_member,
			t.approved AS topic_approved, 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 = m.id_board)
			LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
		WHERE m.id_msg IN ({array_int:message_list})
			AND m.approved = {int:approved_state}', array('message_list' => $msgs, 'approved_state' => $approve ? 0 : 1));
    $msgs = array();
    $topics = array();
    $topic_changes = array();
    $board_changes = array();
    $notification_topics = array();
    $notification_posts = array();
    $member_post_changes = array();
    while ($row = $db->fetch_assoc($request)) {
        // Easy...
        $msgs[] = $row['id_msg'];
        $topics[] = $row['id_topic'];
        // Ensure our change array exists already.
        if (!isset($topic_changes[$row['id_topic']])) {
            $topic_changes[$row['id_topic']] = array('id_last_msg' => $row['id_last_msg'], 'approved' => $row['topic_approved'], 'replies' => 0, 'unapproved_posts' => 0);
        }
        if (!isset($board_changes[$row['id_board']])) {
            $board_changes[$row['id_board']] = array('posts' => 0, 'topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0);
        }
        // If it's the first message then the topic state changes!
        if ($row['id_msg'] == $row['id_first_msg']) {
            $topic_changes[$row['id_topic']]['approved'] = $approve ? 1 : 0;
            $board_changes[$row['id_board']]['unapproved_topics'] += $approve ? -1 : 1;
            $board_changes[$row['id_board']]['topics'] += $approve ? 1 : -1;
            // Note we need to ensure we announce this topic!
            $notification_topics[] = array('body' => $row['body'], 'subject' => $row['subject'], 'name' => $row['poster_name'], 'board' => $row['id_board'], 'topic' => $row['id_topic'], 'msg' => $row['id_first_msg'], 'poster' => $row['id_member']);
        } else {
            $topic_changes[$row['id_topic']]['replies'] += $approve ? 1 : -1;
            // This will be a post... but don't notify unless it's not followed by approved ones.
            if ($row['id_msg'] > $row['id_last_msg']) {
                $notification_posts[$row['id_topic']][] = array('id' => $row['id_msg'], 'body' => $row['body'], 'subject' => $row['subject'], 'name' => $row['poster_name'], 'topic' => $row['id_topic']);
            }
        }
        // If this is being approved and id_msg is higher than the current id_last_msg then it changes.
        if ($approve && $row['id_msg'] > $topic_changes[$row['id_topic']]['id_last_msg']) {
            $topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_msg'];
        } elseif (!$approve) {
            // Default to the first message and then we'll override in a bit ;)
            $topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_first_msg'];
        }
        $topic_changes[$row['id_topic']]['unapproved_posts'] += $approve ? -1 : 1;
        $board_changes[$row['id_board']]['unapproved_posts'] += $approve ? -1 : 1;
        $board_changes[$row['id_board']]['posts'] += $approve ? 1 : -1;
        // Post count for the user?
        if ($row['id_member'] && empty($row['count_posts'])) {
            $member_post_changes[$row['id_member']] = isset($member_post_changes[$row['id_member']]) ? $member_post_changes[$row['id_member']] + 1 : 1;
        }
    }
    $db->free_result($request);
    if (empty($msgs)) {
        return;
    }
    // Now we have the differences make the changes, first the easy one.
    $db->query('', '
		UPDATE {db_prefix}messages
		SET approved = {int:approved_state}
		WHERE id_msg IN ({array_int:message_list})', array('message_list' => $msgs, 'approved_state' => $approve ? 1 : 0));
    // If we were unapproving find the last msg in the topics...
    if (!$approve) {
        $request = $db->query('', '
			SELECT id_topic, MAX(id_msg) AS id_last_msg
			FROM {db_prefix}messages
			WHERE id_topic IN ({array_int:topic_list})
				AND approved = {int:approved}
			GROUP BY id_topic', array('topic_list' => $topics, 'approved' => 1));
        while ($row = $db->fetch_assoc($request)) {
            $topic_changes[$row['id_topic']]['id_last_msg'] = $row['id_last_msg'];
        }
        $db->free_result($request);
    }
    // ... next the topics...
    foreach ($topic_changes as $id => $changes) {
        $db->query('', '
			UPDATE {db_prefix}topics
			SET
				approved = {int:approved},
				unapproved_posts = CASE WHEN unapproved_posts + {int:unapproved_posts} < 0 THEN 0 ELSE unapproved_posts + {int:unapproved_posts} END,
				num_replies = CASE WHEN num_replies + {int:num_replies} < 0 THEN 0 ELSE num_replies + {int:num_replies} END,
				id_last_msg = {int:id_last_msg}
			WHERE id_topic = {int:id_topic}', array('approved' => $changes['approved'], 'unapproved_posts' => $changes['unapproved_posts'], 'num_replies' => $changes['replies'], 'id_last_msg' => $changes['id_last_msg'], 'id_topic' => $id));
    }
    // ... finally the boards...
    foreach ($board_changes as $id => $changes) {
        $db->query('', '
			UPDATE {db_prefix}boards
			SET
				num_posts = num_posts + {int:num_posts},
				unapproved_posts = CASE WHEN unapproved_posts + {int:unapproved_posts} < 0 THEN 0 ELSE unapproved_posts + {int:unapproved_posts} END,
				num_topics = CASE WHEN num_topics + {int:num_topics} < 0 THEN 0 ELSE num_topics + {int:num_topics} END,
				unapproved_topics = CASE WHEN unapproved_topics + {int:unapproved_topics} < 0 THEN 0 ELSE unapproved_topics + {int:unapproved_topics} END
			WHERE id_board = {int:id_board}', array('num_posts' => $changes['posts'], 'unapproved_posts' => $changes['unapproved_posts'], 'num_topics' => $changes['topics'], 'unapproved_topics' => $changes['unapproved_topics'], 'id_board' => $id));
    }
    // Finally, least importantly, notifications!
    if ($approve) {
        require_once SUBSDIR . '/Notification.subs.php';
        if (!empty($notification_topics)) {
            sendBoardNotifications($notification_topics);
        }
        if (!empty($notification_posts)) {
            sendApprovalNotifications($notification_posts);
        }
        $db->query('', '
			DELETE FROM {db_prefix}approval_queue
			WHERE id_msg IN ({array_int:message_list})
				AND id_attach = {int:id_attach}', array('message_list' => $msgs, 'id_attach' => 0));
    } else {
        $msgInserts = array();
        foreach ($msgs as $msg) {
            $msgInserts[] = array($msg);
        }
        $db->insert('ignore', '{db_prefix}approval_queue', array('id_msg' => 'int'), $msgInserts, array('id_msg'));
    }
    if (!empty($modSettings['mentions_enabled'])) {
        require_once SUBSDIR . '/Mentions.subs.php';
        toggleMentionsApproval($msgs, $approve);
    }
    // Update the last messages on the boards...
    updateLastMessages(array_keys($board_changes));
    // Post count for the members?
    if (!empty($member_post_changes)) {
        foreach ($member_post_changes as $id_member => $count_change) {
            updateMemberData($id_member, array('posts' => 'posts ' . ($approve ? '+' : '-') . ' ' . $count_change));
        }
    }
    return true;
}
Exemplo n.º 2
0
/**
 * Allows for moderation from the message index.
 * @todo refactor this...
 */
function QuickModeration()
{
    global $sourcedir, $board, $user_info, $modSettings, $smcFunc, $context;
    // Check the session = get or post.
    checkSession('request');
    // Lets go straight to the restore area.
    if (isset($_REQUEST['qaction']) && $_REQUEST['qaction'] == 'restore' && !empty($_REQUEST['topics'])) {
        redirectexit('action=restoretopic;topics=' . implode(',', $_REQUEST['topics']) . ';' . $context['session_var'] . '=' . $context['session_id']);
    }
    if (isset($_SESSION['topicseen_cache'])) {
        $_SESSION['topicseen_cache'] = array();
    }
    // This is going to be needed to send off the notifications and for updateLastMessages().
    require_once $sourcedir . '/Subs-Post.php';
    // Remember the last board they moved things to.
    if (isset($_REQUEST['move_to'])) {
        $_SESSION['move_to_topic'] = $_REQUEST['move_to'];
    }
    // Only a few possible actions.
    $possibleActions = array();
    if (!empty($board)) {
        $boards_can = array('make_sticky' => allowedTo('make_sticky') ? array($board) : array(), 'move_any' => allowedTo('move_any') ? array($board) : array(), 'move_own' => allowedTo('move_own') ? array($board) : array(), 'remove_any' => allowedTo('remove_any') ? array($board) : array(), 'remove_own' => allowedTo('remove_own') ? array($board) : array(), 'lock_any' => allowedTo('lock_any') ? array($board) : array(), 'lock_own' => allowedTo('lock_own') ? array($board) : array(), 'merge_any' => allowedTo('merge_any') ? array($board) : array(), 'approve_posts' => allowedTo('approve_posts') ? array($board) : array());
        $redirect_url = 'board=' . $board . '.' . $_REQUEST['start'];
    } else {
        /**
         * @todo Ugly. There's no getting around this, is there?
         * @todo Maybe just do this on the actions people want to use?
         */
        $boards_can = boardsAllowedTo(array('make_sticky', 'move_any', 'move_own', 'remove_any', 'remove_own', 'lock_any', 'lock_own', 'merge_any', 'approve_posts'), true, false);
        $redirect_url = isset($_POST['redirect_url']) ? $_POST['redirect_url'] : (isset($_SESSION['old_url']) ? $_SESSION['old_url'] : '');
    }
    if (!$user_info['is_guest']) {
        $possibleActions[] = 'markread';
    }
    if (!empty($boards_can['make_sticky']) && !empty($modSettings['enableStickyTopics'])) {
        $possibleActions[] = 'sticky';
    }
    if (!empty($boards_can['move_any']) || !empty($boards_can['move_own'])) {
        $possibleActions[] = 'move';
    }
    if (!empty($boards_can['remove_any']) || !empty($boards_can['remove_own'])) {
        $possibleActions[] = 'remove';
    }
    if (!empty($boards_can['lock_any']) || !empty($boards_can['lock_own'])) {
        $possibleActions[] = 'lock';
    }
    if (!empty($boards_can['merge_any'])) {
        $possibleActions[] = 'merge';
    }
    if (!empty($boards_can['approve_posts'])) {
        $possibleActions[] = 'approve';
    }
    // Two methods: $_REQUEST['actions'] (id_topic => action), and $_REQUEST['topics'] and $_REQUEST['qaction'].
    // (if action is 'move', $_REQUEST['move_to'] or $_REQUEST['move_tos'][$topic] is used.)
    if (!empty($_REQUEST['topics'])) {
        // If the action isn't valid, just quit now.
        if (empty($_REQUEST['qaction']) || !in_array($_REQUEST['qaction'], $possibleActions)) {
            redirectexit($redirect_url);
        }
        // Merge requires all topics as one parameter and can be done at once.
        if ($_REQUEST['qaction'] == 'merge') {
            // Merge requires at least two topics.
            if (empty($_REQUEST['topics']) || count($_REQUEST['topics']) < 2) {
                redirectexit($redirect_url);
            }
            require_once $sourcedir . '/SplitTopics.php';
            return MergeExecute($_REQUEST['topics']);
        }
        // Just convert to the other method, to make it easier.
        foreach ($_REQUEST['topics'] as $topic) {
            $_REQUEST['actions'][(int) $topic] = $_REQUEST['qaction'];
        }
    }
    // Weird... how'd you get here?
    if (empty($_REQUEST['actions'])) {
        redirectexit($redirect_url);
    }
    // Validate each action.
    $temp = array();
    foreach ($_REQUEST['actions'] as $topic => $action) {
        if (in_array($action, $possibleActions)) {
            $temp[(int) $topic] = $action;
        }
    }
    $_REQUEST['actions'] = $temp;
    if (!empty($_REQUEST['actions'])) {
        // Find all topics...
        $request = $smcFunc['db_query']('', '
			SELECT id_topic, id_member_started, id_board, locked, approved, unapproved_posts
			FROM {db_prefix}topics
			WHERE id_topic IN ({array_int:action_topic_ids})
			LIMIT ' . count($_REQUEST['actions']), array('action_topic_ids' => array_keys($_REQUEST['actions'])));
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            if (!empty($board)) {
                if ($row['id_board'] != $board || $modSettings['postmod_active'] && !$row['approved'] && !allowedTo('approve_posts')) {
                    unset($_REQUEST['actions'][$row['id_topic']]);
                }
            } else {
                // Don't allow them to act on unapproved posts they can't see...
                if ($modSettings['postmod_active'] && !$row['approved'] && !in_array(0, $boards_can['approve_posts']) && !in_array($row['id_board'], $boards_can['approve_posts'])) {
                    unset($_REQUEST['actions'][$row['id_topic']]);
                } elseif ($_REQUEST['actions'][$row['id_topic']] == 'sticky' && !in_array(0, $boards_can['make_sticky']) && !in_array($row['id_board'], $boards_can['make_sticky'])) {
                    unset($_REQUEST['actions'][$row['id_topic']]);
                } elseif ($_REQUEST['actions'][$row['id_topic']] == 'move' && !in_array(0, $boards_can['move_any']) && !in_array($row['id_board'], $boards_can['move_any']) && ($row['id_member_started'] != $user_info['id'] || !in_array(0, $boards_can['move_own']) && !in_array($row['id_board'], $boards_can['move_own']))) {
                    unset($_REQUEST['actions'][$row['id_topic']]);
                } elseif ($_REQUEST['actions'][$row['id_topic']] == 'remove' && !in_array(0, $boards_can['remove_any']) && !in_array($row['id_board'], $boards_can['remove_any']) && ($row['id_member_started'] != $user_info['id'] || !in_array(0, $boards_can['remove_own']) && !in_array($row['id_board'], $boards_can['remove_own']))) {
                    unset($_REQUEST['actions'][$row['id_topic']]);
                } elseif ($_REQUEST['actions'][$row['id_topic']] == 'lock' && !in_array(0, $boards_can['lock_any']) && !in_array($row['id_board'], $boards_can['lock_any']) && ($row['id_member_started'] != $user_info['id'] || $row['locked'] == 1 || !in_array(0, $boards_can['lock_own']) && !in_array($row['id_board'], $boards_can['lock_own']))) {
                    unset($_REQUEST['actions'][$row['id_topic']]);
                } elseif ($_REQUEST['actions'][$row['id_topic']] == 'approve' && (!$row['unapproved_posts'] || !in_array(0, $boards_can['approve_posts']) && !in_array($row['id_board'], $boards_can['approve_posts']))) {
                    unset($_REQUEST['actions'][$row['id_topic']]);
                }
            }
        }
        $smcFunc['db_free_result']($request);
    }
    $stickyCache = array();
    $moveCache = array(0 => array(), 1 => array());
    $removeCache = array();
    $lockCache = array();
    $markCache = array();
    $approveCache = array();
    // Separate the actions.
    foreach ($_REQUEST['actions'] as $topic => $action) {
        $topic = (int) $topic;
        if ($action == 'markread') {
            $markCache[] = $topic;
        } elseif ($action == 'sticky') {
            $stickyCache[] = $topic;
        } elseif ($action == 'move') {
            require_once $sourcedir . '/MoveTopic.php';
            moveTopicConcurrence();
            // $moveCache[0] is the topic, $moveCache[1] is the board to move to.
            $moveCache[1][$topic] = (int) (isset($_REQUEST['move_tos'][$topic]) ? $_REQUEST['move_tos'][$topic] : $_REQUEST['move_to']);
            if (empty($moveCache[1][$topic])) {
                continue;
            }
            $moveCache[0][] = $topic;
        } elseif ($action == 'remove') {
            $removeCache[] = $topic;
        } elseif ($action == 'lock') {
            $lockCache[] = $topic;
        } elseif ($action == 'approve') {
            $approveCache[] = $topic;
        }
    }
    if (empty($board)) {
        $affectedBoards = array();
    } else {
        $affectedBoards = array($board => array(0, 0));
    }
    // Do all the stickies...
    if (!empty($stickyCache)) {
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}topics
			SET is_sticky = CASE WHEN is_sticky = {int:is_sticky} THEN 0 ELSE 1 END
			WHERE id_topic IN ({array_int:sticky_topic_ids})', array('sticky_topic_ids' => $stickyCache, 'is_sticky' => 1));
        // Get the board IDs and Sticky status
        $request = $smcFunc['db_query']('', '
			SELECT id_topic, id_board, is_sticky
			FROM {db_prefix}topics
			WHERE id_topic IN ({array_int:sticky_topic_ids})
			LIMIT ' . count($stickyCache), array('sticky_topic_ids' => $stickyCache));
        $stickyCacheBoards = array();
        $stickyCacheStatus = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $stickyCacheBoards[$row['id_topic']] = $row['id_board'];
            $stickyCacheStatus[$row['id_topic']] = empty($row['is_sticky']);
        }
        $smcFunc['db_free_result']($request);
    }
    // Move sucka! (this is, by the by, probably the most complicated part....)
    if (!empty($moveCache[0])) {
        // I know - I just KNOW you're trying to beat the system.  Too bad for you... we CHECK :P.
        $request = $smcFunc['db_query']('', '
			SELECT t.id_topic, t.id_board, b.count_posts
			FROM {db_prefix}topics AS t
				LEFT JOIN {db_prefix}boards AS b ON (t.id_board = b.id_board)
			WHERE t.id_topic IN ({array_int:move_topic_ids})' . (!empty($board) && !allowedTo('move_any') ? '
				AND t.id_member_started = {int:current_member}' : '') . '
			LIMIT ' . count($moveCache[0]), array('current_member' => $user_info['id'], 'move_topic_ids' => $moveCache[0]));
        $moveTos = array();
        $moveCache2 = array();
        $countPosts = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $to = $moveCache[1][$row['id_topic']];
            if (empty($to)) {
                continue;
            }
            // Does this topic's board count the posts or not?
            $countPosts[$row['id_topic']] = empty($row['count_posts']);
            if (!isset($moveTos[$to])) {
                $moveTos[$to] = array();
            }
            $moveTos[$to][] = $row['id_topic'];
            // For reporting...
            $moveCache2[] = array($row['id_topic'], $row['id_board'], $to);
        }
        $smcFunc['db_free_result']($request);
        $moveCache = $moveCache2;
        require_once $sourcedir . '/MoveTopic.php';
        // Do the actual moves...
        foreach ($moveTos as $to => $topics) {
            moveTopics($topics, $to);
        }
        // Does the post counts need to be updated?
        if (!empty($moveTos)) {
            $topicRecounts = array();
            $request = $smcFunc['db_query']('', '
				SELECT id_board, count_posts
				FROM {db_prefix}boards
				WHERE id_board IN ({array_int:move_boards})', array('move_boards' => array_keys($moveTos)));
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                $cp = empty($row['count_posts']);
                // Go through all the topics that are being moved to this board.
                foreach ($moveTos[$row['id_board']] as $topic) {
                    // If both boards have the same value for post counting then no adjustment needs to be made.
                    if ($countPosts[$topic] != $cp) {
                        // If the board being moved to does count the posts then the other one doesn't so add to their post count.
                        $topicRecounts[$topic] = $cp ? '+' : '-';
                    }
                }
            }
            $smcFunc['db_free_result']($request);
            if (!empty($topicRecounts)) {
                $members = array();
                // Get all the members who have posted in the moved topics.
                $request = $smcFunc['db_query']('', '
					SELECT id_member, id_topic
					FROM {db_prefix}messages
					WHERE id_topic IN ({array_int:moved_topic_ids})', array('moved_topic_ids' => array_keys($topicRecounts)));
                while ($row = $smcFunc['db_fetch_assoc']($request)) {
                    if (!isset($members[$row['id_member']])) {
                        $members[$row['id_member']] = 0;
                    }
                    if ($topicRecounts[$row['id_topic']] === '+') {
                        $members[$row['id_member']] += 1;
                    } else {
                        $members[$row['id_member']] -= 1;
                    }
                }
                $smcFunc['db_free_result']($request);
                // And now update them member's post counts
                foreach ($members as $id_member => $post_adj) {
                    updateMemberData($id_member, array('posts' => 'posts + ' . $post_adj));
                }
            }
        }
    }
    // Now delete the topics...
    if (!empty($removeCache)) {
        // They can only delete their own topics. (we wouldn't be here if they couldn't do that..)
        $result = $smcFunc['db_query']('', '
			SELECT id_topic, id_board
			FROM {db_prefix}topics
			WHERE id_topic IN ({array_int:removed_topic_ids})' . (!empty($board) && !allowedTo('remove_any') ? '
				AND id_member_started = {int:current_member}' : '') . '
			LIMIT ' . count($removeCache), array('current_member' => $user_info['id'], 'removed_topic_ids' => $removeCache));
        $removeCache = array();
        $removeCacheBoards = array();
        while ($row = $smcFunc['db_fetch_assoc']($result)) {
            $removeCache[] = $row['id_topic'];
            $removeCacheBoards[$row['id_topic']] = $row['id_board'];
        }
        $smcFunc['db_free_result']($result);
        // Maybe *none* were their own topics.
        if (!empty($removeCache)) {
            // Gotta send the notifications *first*!
            foreach ($removeCache as $topic) {
                // Only log the topic ID if it's not in the recycle board.
                logAction('remove', array(empty($modSettings['recycle_enable']) || $modSettings['recycle_board'] != $removeCacheBoards[$topic] ? 'topic' : 'old_topic_id' => $topic, 'board' => $removeCacheBoards[$topic]));
                sendNotifications($topic, 'remove');
            }
            require_once $sourcedir . '/RemoveTopic.php';
            removeTopics($removeCache);
        }
    }
    // Approve the topics...
    if (!empty($approveCache)) {
        // We need unapproved topic ids and their authors!
        $request = $smcFunc['db_query']('', '
			SELECT id_topic, id_member_started
			FROM {db_prefix}topics
			WHERE id_topic IN ({array_int:approve_topic_ids})
				AND approved = {int:not_approved}
			LIMIT ' . count($approveCache), array('approve_topic_ids' => $approveCache, 'not_approved' => 0));
        $approveCache = array();
        $approveCacheMembers = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $approveCache[] = $row['id_topic'];
            $approveCacheMembers[$row['id_topic']] = $row['id_member_started'];
        }
        $smcFunc['db_free_result']($request);
        // Any topics to approve?
        if (!empty($approveCache)) {
            // Handle the approval part...
            approveTopics($approveCache);
            // Time for some logging!
            foreach ($approveCache as $topic) {
                logAction('approve_topic', array('topic' => $topic, 'member' => $approveCacheMembers[$topic]));
            }
        }
    }
    // And (almost) lastly, lock the topics...
    if (!empty($lockCache)) {
        $lockStatus = array();
        // Gotta make sure they CAN lock/unlock these topics...
        if (!empty($board) && !allowedTo('lock_any')) {
            // Make sure they started the topic AND it isn't already locked by someone with higher priv's.
            $result = $smcFunc['db_query']('', '
				SELECT id_topic, locked, id_board
				FROM {db_prefix}topics
				WHERE id_topic IN ({array_int:locked_topic_ids})
					AND id_member_started = {int:current_member}
					AND locked IN (2, 0)
				LIMIT ' . count($lockCache), array('current_member' => $user_info['id'], 'locked_topic_ids' => $lockCache));
            $lockCache = array();
            $lockCacheBoards = array();
            while ($row = $smcFunc['db_fetch_assoc']($result)) {
                $lockCache[] = $row['id_topic'];
                $lockCacheBoards[$row['id_topic']] = $row['id_board'];
                $lockStatus[$row['id_topic']] = empty($row['locked']);
            }
            $smcFunc['db_free_result']($result);
        } else {
            $result = $smcFunc['db_query']('', '
				SELECT id_topic, locked, id_board
				FROM {db_prefix}topics
				WHERE id_topic IN ({array_int:locked_topic_ids})
				LIMIT ' . count($lockCache), array('locked_topic_ids' => $lockCache));
            $lockCacheBoards = array();
            while ($row = $smcFunc['db_fetch_assoc']($result)) {
                $lockStatus[$row['id_topic']] = empty($row['locked']);
                $lockCacheBoards[$row['id_topic']] = $row['id_board'];
            }
            $smcFunc['db_free_result']($result);
        }
        // It could just be that *none* were their own topics...
        if (!empty($lockCache)) {
            // Alternate the locked value.
            $smcFunc['db_query']('', '
				UPDATE {db_prefix}topics
				SET locked = CASE WHEN locked = {int:is_locked} THEN ' . (allowedTo('lock_any') ? '1' : '2') . ' ELSE 0 END
				WHERE id_topic IN ({array_int:locked_topic_ids})', array('locked_topic_ids' => $lockCache, 'is_locked' => 0));
        }
    }
    if (!empty($markCache)) {
        $markArray = array();
        foreach ($markCache as $topic) {
            $markArray[] = array($modSettings['maxMsgID'], $user_info['id'], $topic);
        }
        $smcFunc['db_insert']('replace', '{db_prefix}log_topics', array('id_msg' => 'int', 'id_member' => 'int', 'id_topic' => 'int'), $markArray, array('id_member', 'id_topic'));
    }
    foreach ($moveCache as $topic) {
        // Didn't actually move anything!
        if (!isset($topic[0])) {
            break;
        }
        logAction('move', array('topic' => $topic[0], 'board_from' => $topic[1], 'board_to' => $topic[2]));
        sendNotifications($topic[0], 'move');
    }
    foreach ($lockCache as $topic) {
        logAction($lockStatus[$topic] ? 'lock' : 'unlock', array('topic' => $topic, 'board' => $lockCacheBoards[$topic]));
        sendNotifications($topic, $lockStatus[$topic] ? 'lock' : 'unlock');
    }
    foreach ($stickyCache as $topic) {
        logAction($stickyCacheStatus[$topic] ? 'unsticky' : 'sticky', array('topic' => $topic, 'board' => $stickyCacheBoards[$topic]));
        sendNotifications($topic, 'sticky');
    }
    updateStats('topic');
    updateStats('message');
    updateSettings(array('calendar_updated' => time()));
    if (!empty($affectedBoards)) {
        updateLastMessages(array_keys($affectedBoards));
    }
    redirectexit($redirect_url);
}
Exemplo n.º 3
0
function move_topic($topic, $board, $newboard, $topicinfo)
{
    global $mobdb;
    // Move the topic and fix the stats
    $mobdb->query('
        UPDATE {db_prefix}topics
        SET ID_BOARD = {int:board}
        WHERE ID_TOPIC = {int:topic}', array('board' => $newboard['id_board'], 'topic' => $topic));
    $mobdb->query('
        UPDATE {db_prefix}calendar
        SET ID_BOARD = {int:board}
        WHERE ID_TOPIC = {int:topic}', array('topic' => $topic, 'board' => $newboard['id_board']));
    $mobdb->query('
        UPDATE {db_prefix}messages
        SET ID_BOARD = {int:board}
        WHERE ID_TOPIC = {int:topic}', array('board' => $newboard['id_board'], 'topic' => $topic));
    $mobdb->query('
        UPDATE {db_prefix}boards
        SET numPosts = numPosts + {int:new_posts},
            numTopics = numTopics + 1
        WHERE ID_BOARD = {int:board}', array('new_posts' => $topicinfo['replies'] + 1, 'board' => $newboard['id_board']));
    $mobdb->query('
        UPDATE {db_prefix}boards
        SET numPosts = numPosts - {int:new_posts},
            numTopics = numTopics - 1
        WHERE ID_BOARD = {int:board}', array('new_posts' => $topicinfo['replies'] + 1, 'board' => $board));
    // Update the last messages
    updateLastMessages(array($board, $newboard['id_board']));
}
Exemplo n.º 4
0
function MergeExecute($topics = array())
{
    global $db_prefix, $user_info, $txt, $context, $scripturl, $sourcedir;
    global $func, $language, $modSettings;
    // The parameters of MergeExecute were set, so this must've been an internal call.
    if (!empty($topics)) {
        isAllowedTo('merge_any');
        loadTemplate('SplitTopics');
    }
    checkSession('request');
    // Handle URLs from MergeIndex.
    if (!empty($_GET['from']) && !empty($_GET['to'])) {
        $topics = array((int) $_GET['from'], (int) $_GET['to']);
    }
    // If we came from a form, the topic IDs came by post.
    if (!empty($_POST['topics']) && is_array($_POST['topics'])) {
        $topics = $_POST['topics'];
    }
    // There's nothing to merge with just one topic...
    if (empty($topics) || !is_array($topics) || count($topics) == 1) {
        fatal_lang_error('merge_need_more_topics');
    }
    // Make sure every topic is numeric, or some nasty things could be done with the DB.
    foreach ($topics as $id => $topic) {
        $topics[$id] = (int) $topic;
    }
    // Get info about the topics and polls that will be merged.
    $request = db_query("\n\t\tSELECT\n\t\t\tt.ID_TOPIC, t.ID_BOARD, t.ID_POLL, t.numViews, t.isSticky,\n\t\t\tm1.subject, m1.posterTime AS time_started, IFNULL(mem1.ID_MEMBER, 0) AS ID_MEMBER_STARTED, IFNULL(mem1.realName, m1.posterName) AS name_started,\n\t\t\tm2.posterTime AS time_updated, IFNULL(mem2.ID_MEMBER, 0) AS ID_MEMBER_UPDATED, IFNULL(mem2.realName, m2.posterName) AS name_updated\n\t\tFROM ({$db_prefix}topics AS t, {$db_prefix}messages AS m1, {$db_prefix}messages AS m2)\n\t\t\tLEFT JOIN {$db_prefix}members AS mem1 ON (mem1.ID_MEMBER = m1.ID_MEMBER)\n\t\t\tLEFT JOIN {$db_prefix}members AS mem2 ON (mem2.ID_MEMBER = m2.ID_MEMBER)\n\t\tWHERE t.ID_TOPIC IN (" . implode(', ', $topics) . ")\n\t\t\tAND m1.ID_MSG = t.ID_FIRST_MSG\n\t\t\tAND m2.ID_MSG = t.ID_LAST_MSG\n\t\tORDER BY t.ID_FIRST_MSG\n\t\tLIMIT " . count($topics), __FILE__, __LINE__);
    if (mysql_num_rows($request) < 2) {
        fatal_lang_error('smf263');
    }
    $num_views = 0;
    $isSticky = 0;
    $boards = array();
    $polls = array();
    while ($row = mysql_fetch_assoc($request)) {
        $topic_data[$row['ID_TOPIC']] = array('id' => $row['ID_TOPIC'], 'board' => $row['ID_BOARD'], 'poll' => $row['ID_POLL'], 'numViews' => $row['numViews'], 'subject' => $row['subject'], 'started' => array('time' => timeformat($row['time_started']), 'timestamp' => forum_time(true, $row['time_started']), 'href' => empty($row['ID_MEMBER_STARTED']) ? '' : $scripturl . '?action=profile;u=' . $row['ID_MEMBER_STARTED'], 'link' => empty($row['ID_MEMBER_STARTED']) ? $row['name_started'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['ID_MEMBER_STARTED'] . '">' . $row['name_started'] . '</a>'), 'updated' => array('time' => timeformat($row['time_updated']), 'timestamp' => forum_time(true, $row['time_updated']), 'href' => empty($row['ID_MEMBER_UPDATED']) ? '' : $scripturl . '?action=profile;u=' . $row['ID_MEMBER_UPDATED'], 'link' => empty($row['ID_MEMBER_UPDATED']) ? $row['name_updated'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['ID_MEMBER_UPDATED'] . '">' . $row['name_updated'] . '</a>'));
        $num_views += $row['numViews'];
        $boards[] = $row['ID_BOARD'];
        // If there's no poll, ID_POLL == 0...
        if ($row['ID_POLL'] > 0) {
            $polls[] = $row['ID_POLL'];
        }
        // Store the ID_TOPIC with the lowest ID_FIRST_MSG.
        if (empty($firstTopic)) {
            $firstTopic = $row['ID_TOPIC'];
        }
        $isSticky = max($isSticky, $row['isSticky']);
    }
    mysql_free_result($request);
    $boards = array_values(array_unique($boards));
    // Get the boards a user is allowed to merge in.
    $merge_boards = boardsAllowedTo('merge_any');
    if (empty($merge_boards)) {
        fatal_lang_error('cannot_merge_any');
    }
    // Make sure they can see all boards....
    $request = db_query("\n\t\tSELECT b.ID_BOARD\n\t\tFROM {$db_prefix}boards AS b\n\t\tWHERE b.ID_BOARD IN (" . implode(', ', $boards) . ")\n\t\t\tAND {$user_info['query_see_board']}" . (!in_array(0, $merge_boards) ? "\n\t\t\tAND b.ID_BOARD IN (" . implode(', ', $merge_boards) . ")" : '') . "\n\t\tLIMIT " . count($boards), __FILE__, __LINE__);
    // If the number of boards that's in the output isn't exactly the same as we've put in there, you're in trouble.
    if (mysql_num_rows($request) != count($boards)) {
        fatal_lang_error('smf232');
    }
    mysql_free_result($request);
    if (empty($_REQUEST['sa']) || $_REQUEST['sa'] == 'options') {
        if (count($polls) > 1) {
            $request = db_query("\n\t\t\t\tSELECT t.ID_TOPIC, t.ID_POLL, m.subject, p.question\n\t\t\t\tFROM ({$db_prefix}polls AS p, {$db_prefix}topics AS t, {$db_prefix}messages AS m)\n\t\t\t\tWHERE p.ID_POLL IN (" . implode(', ', $polls) . ")\n\t\t\t\t\tAND t.ID_POLL = p.ID_POLL\n\t\t\t\t\tAND m.ID_MSG = t.ID_FIRST_MSG\n\t\t\t\tLIMIT " . count($polls), __FILE__, __LINE__);
            while ($row = mysql_fetch_assoc($request)) {
                $context['polls'][] = array('id' => $row['ID_POLL'], 'topic' => array('id' => $row['ID_TOPIC'], 'subject' => $row['subject']), 'question' => $row['question'], 'selected' => $row['ID_TOPIC'] == $firstTopic);
            }
            mysql_free_result($request);
        }
        if (count($boards) > 1) {
            $request = db_query("\n\t\t\t\tSELECT ID_BOARD, name\n\t\t\t\tFROM {$db_prefix}boards\n\t\t\t\tWHERE ID_BOARD IN (" . implode(', ', $boards) . ")\n\t\t\t\tORDER BY name\n\t\t\t\tLIMIT " . count($boards), __FILE__, __LINE__);
            while ($row = mysql_fetch_assoc($request)) {
                $context['boards'][] = array('id' => $row['ID_BOARD'], 'name' => $row['name'], 'selected' => $row['ID_BOARD'] == $topic_data[$firstTopic]['board']);
            }
            mysql_free_result($request);
        }
        $context['topics'] = $topic_data;
        foreach ($topic_data as $id => $topic) {
            $context['topics'][$id]['selected'] = $topic['id'] == $firstTopic;
        }
        $context['page_title'] = $txt['smf252'];
        $context['sub_template'] = 'merge_extra_options';
        return;
    }
    // Determine target board.
    $target_board = count($boards) > 1 ? (int) $_REQUEST['board'] : $boards[0];
    if (!in_array($target_board, $boards)) {
        fatal_lang_error('smf232');
    }
    // Determine which poll will survive and which polls won't.
    $target_poll = count($polls) > 1 ? (int) $_POST['poll'] : (count($polls) == 1 ? $polls[0] : 0);
    if ($target_poll > 0 && !in_array($target_poll, $polls)) {
        fatal_lang_error(1, false);
    }
    $deleted_polls = empty($target_poll) ? $polls : array_diff($polls, array($target_poll));
    // Determine the subject of the newly merged topic - was a custom subject specified?
    if (empty($_POST['subject']) && isset($_POST['custom_subject']) && $_POST['custom_subject'] != '') {
        $target_subject = $func['htmlspecialchars']($_POST['custom_subject']);
    } elseif (!empty($topic_data[(int) $_POST['subject']]['subject'])) {
        $target_subject = addslashes($topic_data[(int) $_POST['subject']]['subject']);
    } else {
        $target_subject = addslashes($topic_data[$firstTopic]['subject']);
    }
    // Get the first and last message and the number of messages....
    $request = db_query("\n\t\tSELECT MIN(ID_MSG), MAX(ID_MSG), COUNT(ID_MSG) - 1\n\t\tFROM {$db_prefix}messages\n\t\tWHERE ID_TOPIC IN (" . implode(', ', $topics) . ")", __FILE__, __LINE__);
    list($first_msg, $last_msg, $num_replies) = mysql_fetch_row($request);
    mysql_free_result($request);
    // Get the member ID of the first and last message.
    $request = db_query("\n\t\tSELECT ID_MEMBER\n\t\tFROM {$db_prefix}messages\n\t\tWHERE ID_MSG IN ({$first_msg}, {$last_msg})\n\t\tORDER BY ID_MSG\n\t\tLIMIT 2", __FILE__, __LINE__);
    list($member_started) = mysql_fetch_row($request);
    list($member_updated) = mysql_fetch_row($request);
    mysql_free_result($request);
    // Assign the first topic ID to be the merged topic.
    $ID_TOPIC = min($topics);
    // Delete the remaining topics.
    $deleted_topics = array_diff($topics, array($ID_TOPIC));
    db_query("\n\t\tDELETE FROM {$db_prefix}topics\n\t\tWHERE ID_TOPIC IN (" . implode(', ', $deleted_topics) . ")\n\t\tLIMIT " . count($deleted_topics), __FILE__, __LINE__);
    db_query("\n\t\tDELETE FROM {$db_prefix}log_search_subjects\n\t\tWHERE ID_TOPIC IN (" . implode(', ', $deleted_topics) . ")", __FILE__, __LINE__);
    // Asssign the properties of the newly merged topic.
    db_query("\n\t\tUPDATE {$db_prefix}topics\n\t\tSET\n\t\t\tID_BOARD = {$target_board},\n\t\t\tID_MEMBER_STARTED = {$member_started},\n\t\t\tID_MEMBER_UPDATED = {$member_updated},\n\t\t\tID_FIRST_MSG = {$first_msg},\n\t\t\tID_LAST_MSG = {$last_msg},\n\t\t\tID_POLL = {$target_poll},\n\t\t\tnumReplies = {$num_replies},\n\t\t\tnumViews = {$num_views},\n\t\t\tisSticky = {$isSticky}\n\t\tWHERE ID_TOPIC = {$ID_TOPIC}\n\t\tLIMIT 1", __FILE__, __LINE__);
    // Grab the response prefix (like 'Re: ') in the default forum language.
    if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix'))) {
        if ($language === $user_info['language']) {
            $context['response_prefix'] = $txt['response_prefix'];
        } else {
            loadLanguage('index', $language, false);
            $context['response_prefix'] = $txt['response_prefix'];
            loadLanguage('index');
        }
        cache_put_data('response_prefix', $context['response_prefix'], 600);
    }
    // Change the topic IDs of all messages that will be merged.  Also adjust subjects if 'enforce subject' was checked.
    db_query("\n\t\tUPDATE {$db_prefix}messages\n\t\tSET\n\t\t\tID_TOPIC = {$ID_TOPIC},\n\t\t\tID_BOARD = {$target_board}" . (!empty($_POST['enforce_subject']) ? ",\n\t\t\tsubject = '{$context['response_prefix']}{$target_subject}'" : '') . "\n\t\tWHERE ID_TOPIC IN (" . implode(', ', $topics) . ")", __FILE__, __LINE__);
    // Change the subject of the first message...
    db_query("\n\t\tUPDATE {$db_prefix}messages\n\t\tSET subject = '{$target_subject}'\n\t\tWHERE ID_MSG = {$first_msg}\n\t\tLIMIT 1", __FILE__, __LINE__);
    // Adjust all calendar events to point to the new topic.
    db_query("\n\t\tUPDATE {$db_prefix}calendar\n\t\tSET\n\t\t\tID_TOPIC = {$ID_TOPIC},\n\t\t\tID_BOARD = {$target_board}\n\t\tWHERE ID_TOPIC IN (" . implode(', ', $deleted_topics) . ")", __FILE__, __LINE__);
    // Merge log topic entries.
    $request = db_query("\n\t\tSELECT ID_MEMBER, MIN(ID_MSG) AS new_ID_MSG\n\t\tFROM {$db_prefix}log_topics\n\t\tWHERE ID_TOPIC IN (" . implode(', ', $topics) . ")\n\t\tGROUP BY ID_MEMBER", __FILE__, __LINE__);
    if (mysql_num_rows($request) > 0) {
        $replaceEntries = array();
        while ($row = mysql_fetch_assoc($request)) {
            $replaceEntries[] = "({$row['ID_MEMBER']}, {$ID_TOPIC}, {$row['new_ID_MSG']})";
        }
        db_query("\n\t\t\tREPLACE INTO {$db_prefix}log_topics\n\t\t\t\t(ID_MEMBER, ID_TOPIC, ID_MSG)\n\t\t\tVALUES " . implode(', ', $replaceEntries), __FILE__, __LINE__);
        unset($replaceEntries);
        // Get rid of the old log entries.
        db_query("\n\t\t\tDELETE FROM {$db_prefix}log_topics\n\t\t\tWHERE ID_TOPIC IN (" . implode(', ', $deleted_topics) . ")", __FILE__, __LINE__);
    }
    mysql_free_result($request);
    // Merge topic notifications.
    if (!empty($_POST['notifications']) && is_array($_POST['notifications'])) {
        // Check if the notification array contains valid topics.
        if (count(array_diff($_POST['notifications'], $topics)) > 0) {
            fatal_lang_error('smf232');
        }
        $request = db_query("\n\t\t\tSELECT ID_MEMBER, MAX(sent) AS sent\n\t\t\tFROM {$db_prefix}log_notify\n\t\t\tWHERE ID_TOPIC IN (" . implode(', ', $_POST['notifications']) . ")\n\t\t\tGROUP BY ID_MEMBER", __FILE__, __LINE__);
        if (mysql_num_rows($request) > 0) {
            $replaceEntries = array();
            while ($row = mysql_fetch_assoc($request)) {
                $replaceEntries[] = "({$row['ID_MEMBER']}, {$ID_TOPIC}, 0, {$row['sent']})";
            }
            db_query("\n\t\t\t\tREPLACE INTO {$db_prefix}log_notify\n\t\t\t\t\t(ID_MEMBER, ID_TOPIC, ID_BOARD, sent)\n\t\t\t\tVALUES " . implode(', ', $replaceEntries), __FILE__, __LINE__);
            unset($replaceEntries);
            db_query("\n\t\t\t\tDELETE FROM {$db_prefix}log_topics\n\t\t\t\tWHERE ID_TOPIC IN (" . implode(', ', $deleted_topics) . ")", __FILE__, __LINE__);
        }
        mysql_free_result($request);
    }
    // Get rid of the redundant polls.
    if (!empty($deleted_polls)) {
        db_query("\n\t\t\tDELETE FROM {$db_prefix}polls\n\t\t\tWHERE ID_POLL IN (" . implode(', ', $deleted_polls) . ")\n\t\t\tLIMIT 1", __FILE__, __LINE__);
        db_query("\n\t\t\tDELETE FROM {$db_prefix}poll_choices\n\t\t\tWHERE ID_POLL IN (" . implode(', ', $deleted_polls) . ")", __FILE__, __LINE__);
        db_query("\n\t\t\tDELETE FROM {$db_prefix}log_polls\n\t\t\tWHERE ID_POLL IN (" . implode(', ', $deleted_polls) . ")", __FILE__, __LINE__);
    }
    // Fix the board totals.
    if (count($boards) > 1) {
        $request = db_query("\n\t\t\tSELECT ID_BOARD, COUNT(*) AS numTopics, SUM(numReplies) + COUNT(*) AS numPosts\n\t\t\tFROM {$db_prefix}topics\n\t\t\tWHERE ID_BOARD IN (" . implode(', ', $boards) . ")\n\t\t\tGROUP BY ID_BOARD\n\t\t\tLIMIT " . count($boards), __FILE__, __LINE__);
        while ($row = mysql_fetch_assoc($request)) {
            db_query("\n\t\t\t\tUPDATE {$db_prefix}boards\n\t\t\t\tSET\n\t\t\t\t\tnumPosts = {$row['numPosts']},\n\t\t\t\t\tnumTopics = {$row['numTopics']}\n\t\t\t\tWHERE ID_BOARD = {$row['ID_BOARD']}\n\t\t\t\tLIMIT 1", __FILE__, __LINE__);
        }
        mysql_free_result($request);
    } else {
        db_query("\n\t\t\tUPDATE {$db_prefix}boards\n\t\t\tSET numTopics = IF(" . (count($topics) - 1) . " > numTopics, 0, numTopics - " . (count($topics) - 1) . ")\n\t\t\tWHERE ID_BOARD = {$target_board}\n\t\t\tLIMIT 1", __FILE__, __LINE__);
    }
    require_once $sourcedir . '/Subs-Post.php';
    // Update all the statistics.
    updateStats('topic');
    updateStats('subject', $ID_TOPIC, $target_subject);
    updateLastMessages($boards);
    logAction('merge', array('topic' => $ID_TOPIC));
    // Notify people that these topics have been merged?
    sendNotifications($ID_TOPIC, 'merge');
    // Send them to the all done page.
    redirectexit('action=mergetopics;sa=done;to=' . $ID_TOPIC . ';targetboard=' . $target_board);
}
Exemplo n.º 5
0
/**
 * set merge options and do the actual merge of two or more topics.
 *
 * the merge options screen:
 * * shows topics to be merged and allows to set some merge options.
 * * is accessed by ?action=mergetopics;sa=options.and can also internally be called by QuickModeration() (Subs-Boards.php).
 * * uses 'merge_extra_options' sub template of the SplitTopics template.
 *
 * the actual merge:
 * * is accessed with ?action=mergetopics;sa=execute.
 * * updates the statistics to reflect the merge.
 * * logs the action in the moderation log.
 * * sends a notification is sent to all users monitoring this topic.
 * * redirects to ?action=mergetopics;sa=done.
 * @param array $topics = array()
 */
function MergeExecute($topics = array())
{
    global $user_info, $txt, $context, $scripturl, $sourcedir;
    global $smcFunc, $language, $modSettings;
    // Check the session.
    checkSession('request');
    // Handle URLs from MergeIndex.
    if (!empty($_GET['from']) && !empty($_GET['to'])) {
        $topics = array((int) $_GET['from'], (int) $_GET['to']);
    }
    // If we came from a form, the topic IDs came by post.
    if (!empty($_POST['topics']) && is_array($_POST['topics'])) {
        $topics = $_POST['topics'];
    }
    // There's nothing to merge with just one topic...
    if (empty($topics) || !is_array($topics) || count($topics) == 1) {
        fatal_lang_error('merge_need_more_topics');
    }
    // Make sure every topic is numeric, or some nasty things could be done with the DB.
    foreach ($topics as $id => $topic) {
        $topics[$id] = (int) $topic;
    }
    // Joy of all joys, make sure they're not pi**ing about with unapproved topics they can't see :P
    if ($modSettings['postmod_active']) {
        $can_approve_boards = boardsAllowedTo('approve_posts');
    }
    // Get info about the topics and polls that will be merged.
    $request = $smcFunc['db_query']('', '
		SELECT
			t.id_topic, t.id_board, t.id_poll, t.num_views, t.is_sticky, t.approved, t.num_replies, t.unapproved_posts,
			m1.subject, m1.poster_time AS time_started, IFNULL(mem1.id_member, 0) AS id_member_started, IFNULL(mem1.real_name, m1.poster_name) AS name_started,
			m2.poster_time AS time_updated, IFNULL(mem2.id_member, 0) AS id_member_updated, IFNULL(mem2.real_name, m2.poster_name) AS name_updated
		FROM {db_prefix}topics AS t
			INNER JOIN {db_prefix}messages AS m1 ON (m1.id_msg = t.id_first_msg)
			INNER JOIN {db_prefix}messages AS m2 ON (m2.id_msg = t.id_last_msg)
			LEFT JOIN {db_prefix}members AS mem1 ON (mem1.id_member = m1.id_member)
			LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = m2.id_member)
		WHERE t.id_topic IN ({array_int:topic_list})
		ORDER BY t.id_first_msg
		LIMIT ' . count($topics), array('topic_list' => $topics));
    if ($smcFunc['db_num_rows']($request) < 2) {
        fatal_lang_error('no_topic_id');
    }
    $num_views = 0;
    $is_sticky = 0;
    $boardTotals = array();
    $boards = array();
    $polls = array();
    $firstTopic = 0;
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        // Make a note for the board counts...
        if (!isset($boardTotals[$row['id_board']])) {
            $boardTotals[$row['id_board']] = array('posts' => 0, 'topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0);
        }
        // We can't see unapproved topics here?
        if ($modSettings['postmod_active'] && !$row['approved'] && $can_approve_boards != array(0) && in_array($row['id_board'], $can_approve_boards)) {
            continue;
        } elseif (!$row['approved']) {
            $boardTotals[$row['id_board']]['unapproved_topics']++;
        } else {
            $boardTotals[$row['id_board']]['topics']++;
        }
        $boardTotals[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts'];
        $boardTotals[$row['id_board']]['posts'] += $row['num_replies'] + ($row['approved'] ? 1 : 0);
        $topic_data[$row['id_topic']] = array('id' => $row['id_topic'], 'board' => $row['id_board'], 'poll' => $row['id_poll'], 'num_views' => $row['num_views'], 'subject' => $row['subject'], 'started' => array('time' => timeformat($row['time_started']), 'timestamp' => forum_time(true, $row['time_started']), 'href' => empty($row['id_member_started']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_started'], 'link' => empty($row['id_member_started']) ? $row['name_started'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_started'] . '">' . $row['name_started'] . '</a>'), 'updated' => array('time' => timeformat($row['time_updated']), 'timestamp' => forum_time(true, $row['time_updated']), 'href' => empty($row['id_member_updated']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_updated'], 'link' => empty($row['id_member_updated']) ? $row['name_updated'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_updated'] . '">' . $row['name_updated'] . '</a>'));
        $num_views += $row['num_views'];
        $boards[] = $row['id_board'];
        // If there's no poll, id_poll == 0...
        if ($row['id_poll'] > 0) {
            $polls[] = $row['id_poll'];
        }
        // Store the id_topic with the lowest id_first_msg.
        if (empty($firstTopic)) {
            $firstTopic = $row['id_topic'];
        }
        $is_sticky = max($is_sticky, $row['is_sticky']);
    }
    $smcFunc['db_free_result']($request);
    // If we didn't get any topics then they've been messing with unapproved stuff.
    if (empty($topic_data)) {
        fatal_lang_error('no_topic_id');
    }
    $boards = array_values(array_unique($boards));
    // The parameters of MergeExecute were set, so this must've been an internal call.
    if (!empty($topics)) {
        isAllowedTo('merge_any', $boards);
        loadTemplate('SplitTopics');
    }
    // Get the boards a user is allowed to merge in.
    $merge_boards = boardsAllowedTo('merge_any');
    if (empty($merge_boards)) {
        fatal_lang_error('cannot_merge_any', 'user');
    }
    // Make sure they can see all boards....
    $request = $smcFunc['db_query']('', '
		SELECT b.id_board
		FROM {db_prefix}boards AS b
		WHERE b.id_board IN ({array_int:boards})
			AND {query_see_board}' . (!in_array(0, $merge_boards) ? '
			AND b.id_board IN ({array_int:merge_boards})' : '') . '
		LIMIT ' . count($boards), array('boards' => $boards, 'merge_boards' => $merge_boards));
    // If the number of boards that's in the output isn't exactly the same as we've put in there, you're in trouble.
    if ($smcFunc['db_num_rows']($request) != count($boards)) {
        fatal_lang_error('no_board');
    }
    $smcFunc['db_free_result']($request);
    if (empty($_REQUEST['sa']) || $_REQUEST['sa'] == 'options') {
        if (count($polls) > 1) {
            $request = $smcFunc['db_query']('', '
				SELECT t.id_topic, t.id_poll, m.subject, p.question
				FROM {db_prefix}polls AS p
					INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll)
					INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
				WHERE p.id_poll IN ({array_int:polls})
				LIMIT ' . count($polls), array('polls' => $polls));
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                $context['polls'][] = array('id' => $row['id_poll'], 'topic' => array('id' => $row['id_topic'], 'subject' => $row['subject']), 'question' => $row['question'], 'selected' => $row['id_topic'] == $firstTopic);
            }
            $smcFunc['db_free_result']($request);
        }
        if (count($boards) > 1) {
            $request = $smcFunc['db_query']('', '
				SELECT id_board, name
				FROM {db_prefix}boards
				WHERE id_board IN ({array_int:boards})
				ORDER BY name
				LIMIT ' . count($boards), array('boards' => $boards));
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                $context['boards'][] = array('id' => $row['id_board'], 'name' => $row['name'], 'selected' => $row['id_board'] == $topic_data[$firstTopic]['board']);
            }
            $smcFunc['db_free_result']($request);
        }
        $context['topics'] = $topic_data;
        foreach ($topic_data as $id => $topic) {
            $context['topics'][$id]['selected'] = $topic['id'] == $firstTopic;
        }
        $context['page_title'] = $txt['merge'];
        $context['sub_template'] = 'merge_extra_options';
        return;
    }
    // Determine target board.
    $target_board = count($boards) > 1 ? (int) $_REQUEST['board'] : $boards[0];
    if (!in_array($target_board, $boards)) {
        fatal_lang_error('no_board');
    }
    // Determine which poll will survive and which polls won't.
    $target_poll = count($polls) > 1 ? (int) $_POST['poll'] : (count($polls) == 1 ? $polls[0] : 0);
    if ($target_poll > 0 && !in_array($target_poll, $polls)) {
        fatal_lang_error('no_access', false);
    }
    $deleted_polls = empty($target_poll) ? $polls : array_diff($polls, array($target_poll));
    // Determine the subject of the newly merged topic - was a custom subject specified?
    if (empty($_POST['subject']) && isset($_POST['custom_subject']) && $_POST['custom_subject'] != '') {
        $target_subject = strtr($smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['custom_subject'])), array("\r" => '', "\n" => '', "\t" => ''));
        // Keep checking the length.
        if ($smcFunc['strlen']($target_subject) > 100) {
            $target_subject = $smcFunc['substr']($target_subject, 0, 100);
        }
        // Nothing left - odd but pick the first topics subject.
        if ($target_subject == '') {
            $target_subject = $topic_data[$firstTopic]['subject'];
        }
    } elseif (!empty($topic_data[(int) $_POST['subject']]['subject'])) {
        $target_subject = $topic_data[(int) $_POST['subject']]['subject'];
    } else {
        $target_subject = $topic_data[$firstTopic]['subject'];
    }
    // Get the first and last message and the number of messages....
    $request = $smcFunc['db_query']('', '
		SELECT approved, MIN(id_msg) AS first_msg, MAX(id_msg) AS last_msg, COUNT(*) AS message_count
		FROM {db_prefix}messages
		WHERE id_topic IN ({array_int:topics})
		GROUP BY approved
		ORDER BY approved DESC', array('topics' => $topics));
    $topic_approved = 1;
    $first_msg = 0;
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        // If this is approved, or is fully unapproved.
        if ($row['approved'] || !isset($first_msg)) {
            $first_msg = $row['first_msg'];
            $last_msg = $row['last_msg'];
            if ($row['approved']) {
                $num_replies = $row['message_count'] - 1;
                $num_unapproved = 0;
            } else {
                $topic_approved = 0;
                $num_replies = 0;
                $num_unapproved = $row['message_count'];
            }
        } else {
            // If this has a lower first_msg then the first post is not approved and hence the number of replies was wrong!
            if ($first_msg > $row['first_msg']) {
                $first_msg = $row['first_msg'];
                $num_replies++;
                $topic_approved = 0;
            }
            $num_unapproved = $row['message_count'];
        }
    }
    $smcFunc['db_free_result']($request);
    // Ensure we have a board stat for the target board.
    if (!isset($boardTotals[$target_board])) {
        $boardTotals[$target_board] = array('posts' => 0, 'topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0);
    }
    // Fix the topic count stuff depending on what the new one counts as.
    if ($topic_approved) {
        $boardTotals[$target_board]['topics']--;
    } else {
        $boardTotals[$target_board]['unapproved_topics']--;
    }
    $boardTotals[$target_board]['unapproved_posts'] -= $num_unapproved;
    $boardTotals[$target_board]['posts'] -= $topic_approved ? $num_replies + 1 : $num_replies;
    // Get the member ID of the first and last message.
    $request = $smcFunc['db_query']('', '
		SELECT id_member
		FROM {db_prefix}messages
		WHERE id_msg IN ({int:first_msg}, {int:last_msg})
		ORDER BY id_msg
		LIMIT 2', array('first_msg' => $first_msg, 'last_msg' => $last_msg));
    list($member_started) = $smcFunc['db_fetch_row']($request);
    list($member_updated) = $smcFunc['db_fetch_row']($request);
    // First and last message are the same, so only row was returned.
    if ($member_updated === NULL) {
        $member_updated = $member_started;
    }
    $smcFunc['db_free_result']($request);
    // Obtain all the message ids we are going to affect.
    $affected_msgs = array();
    $request = $smcFunc['db_query']('', '
		SELECT id_msg
		FROM {db_prefix}messages
		WHERE id_topic IN ({array_int:topic_list})', array('topic_list' => $topics));
    while ($row = $smcFunc['db_fetch_row']($request)) {
        $affected_msgs[] = $row[0];
    }
    $smcFunc['db_free_result']($request);
    // Assign the first topic ID to be the merged topic.
    $id_topic = min($topics);
    // Delete the remaining topics.
    $deleted_topics = array_diff($topics, array($id_topic));
    $smcFunc['db_query']('', '
		DELETE FROM {db_prefix}topics
		WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics));
    $smcFunc['db_query']('', '
		DELETE FROM {db_prefix}log_search_subjects
		WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics));
    // Asssign the properties of the newly merged topic.
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}topics
		SET
			id_board = {int:id_board},
			id_member_started = {int:id_member_started},
			id_member_updated = {int:id_member_updated},
			id_first_msg = {int:id_first_msg},
			id_last_msg = {int:id_last_msg},
			id_poll = {int:id_poll},
			num_replies = {int:num_replies},
			unapproved_posts = {int:unapproved_posts},
			num_views = {int:num_views},
			is_sticky = {int:is_sticky},
			approved = {int:approved}
		WHERE id_topic = {int:id_topic}', array('id_board' => $target_board, 'is_sticky' => $is_sticky, 'approved' => $topic_approved, 'id_topic' => $id_topic, 'id_member_started' => $member_started, 'id_member_updated' => $member_updated, 'id_first_msg' => $first_msg, 'id_last_msg' => $last_msg, 'id_poll' => $target_poll, 'num_replies' => $num_replies, 'unapproved_posts' => $num_unapproved, 'num_views' => $num_views));
    // Grab the response prefix (like 'Re: ') in the default forum language.
    if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix'))) {
        if ($language === $user_info['language']) {
            $context['response_prefix'] = $txt['response_prefix'];
        } else {
            loadLanguage('index', $language, false);
            $context['response_prefix'] = $txt['response_prefix'];
            loadLanguage('index');
        }
        cache_put_data('response_prefix', $context['response_prefix'], 600);
    }
    // Change the topic IDs of all messages that will be merged.  Also adjust subjects if 'enforce subject' was checked.
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}messages
		SET
			id_topic = {int:id_topic},
			id_board = {int:target_board}' . (empty($_POST['enforce_subject']) ? '' : ',
			subject = {string:subject}') . '
		WHERE id_topic IN ({array_int:topic_list})', array('topic_list' => $topics, 'id_topic' => $id_topic, 'target_board' => $target_board, 'subject' => $context['response_prefix'] . $target_subject));
    // Any reported posts should reflect the new board.
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}log_reported
		SET
			id_topic = {int:id_topic},
			id_board = {int:target_board}
		WHERE id_topic IN ({array_int:topics_list})', array('topics_list' => $topics, 'id_topic' => $id_topic, 'target_board' => $target_board));
    // Change the subject of the first message...
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}messages
		SET subject = {string:target_subject}
		WHERE id_msg = {int:first_msg}', array('first_msg' => $first_msg, 'target_subject' => $target_subject));
    // Adjust all calendar events to point to the new topic.
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}calendar
		SET
			id_topic = {int:id_topic},
			id_board = {int:target_board}
		WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics, 'id_topic' => $id_topic, 'target_board' => $target_board));
    // Merge log topic entries.
    $request = $smcFunc['db_query']('', '
		SELECT id_member, MIN(id_msg) AS new_id_msg
		FROM {db_prefix}log_topics
		WHERE id_topic IN ({array_int:topics})
		GROUP BY id_member', array('topics' => $topics));
    if ($smcFunc['db_num_rows']($request) > 0) {
        $replaceEntries = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $replaceEntries[] = array($row['id_member'], $id_topic, $row['new_id_msg']);
        }
        $smcFunc['db_insert']('replace', '{db_prefix}log_topics', array('id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int'), $replaceEntries, array('id_member', 'id_topic'));
        unset($replaceEntries);
        // Get rid of the old log entries.
        $smcFunc['db_query']('', '
			DELETE FROM {db_prefix}log_topics
			WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics));
    }
    $smcFunc['db_free_result']($request);
    // Merge topic notifications.
    $notifications = isset($_POST['notifications']) && is_array($_POST['notifications']) ? array_intersect($topics, $_POST['notifications']) : array();
    if (!empty($notifications)) {
        $request = $smcFunc['db_query']('', '
			SELECT id_member, MAX(sent) AS sent
			FROM {db_prefix}log_notify
			WHERE id_topic IN ({array_int:topics_list})
			GROUP BY id_member', array('topics_list' => $notifications));
        if ($smcFunc['db_num_rows']($request) > 0) {
            $replaceEntries = array();
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                $replaceEntries[] = array($row['id_member'], $id_topic, 0, $row['sent']);
            }
            $smcFunc['db_insert']('replace', '{db_prefix}log_notify', array('id_member' => 'int', 'id_topic' => 'int', 'id_board' => 'int', 'sent' => 'int'), $replaceEntries, array('id_member', 'id_topic', 'id_board'));
            unset($replaceEntries);
            $smcFunc['db_query']('', '
				DELETE FROM {db_prefix}log_topics
				WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics));
        }
        $smcFunc['db_free_result']($request);
    }
    // Get rid of the redundant polls.
    if (!empty($deleted_polls)) {
        $smcFunc['db_query']('', '
			DELETE FROM {db_prefix}polls
			WHERE id_poll IN ({array_int:deleted_polls})', array('deleted_polls' => $deleted_polls));
        $smcFunc['db_query']('', '
			DELETE FROM {db_prefix}poll_choices
			WHERE id_poll IN ({array_int:deleted_polls})', array('deleted_polls' => $deleted_polls));
        $smcFunc['db_query']('', '
			DELETE FROM {db_prefix}log_polls
			WHERE id_poll IN ({array_int:deleted_polls})', array('deleted_polls' => $deleted_polls));
    }
    // Cycle through each board...
    foreach ($boardTotals as $id_board => $stats) {
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}boards
			SET
				num_topics = CASE WHEN {int:topics} > num_topics THEN 0 ELSE num_topics - {int:topics} END,
				unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END,
				num_posts = CASE WHEN {int:posts} > num_posts THEN 0 ELSE num_posts - {int:posts} END,
				unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END
			WHERE id_board = {int:id_board}', array('id_board' => $id_board, 'topics' => $stats['topics'], 'unapproved_topics' => $stats['unapproved_topics'], 'posts' => $stats['posts'], 'unapproved_posts' => $stats['unapproved_posts']));
    }
    // Determine the board the final topic resides in
    $request = $smcFunc['db_query']('', '
		SELECT id_board
		FROM {db_prefix}topics
		WHERE id_topic = {int:id_topic}
		LIMIT 1', array('id_topic' => $id_topic));
    list($id_board) = $smcFunc['db_fetch_row']($request);
    $smcFunc['db_free_result']($request);
    require_once $sourcedir . '/Subs-Post.php';
    // Update all the statistics.
    updateStats('topic');
    updateStats('subject', $id_topic, $target_subject);
    updateLastMessages($boards);
    logAction('merge', array('topic' => $id_topic, 'board' => $id_board));
    // Notify people that these topics have been merged?
    sendNotifications($id_topic, 'merge');
    // If there's a search index that needs updating, update it...
    require_once $sourcedir . '/Search.php';
    $searchAPI = findSearchAPI();
    if (is_callable(array($searchAPI, 'topicMerge'))) {
        $searchAPI->topicMerge($id_topic, $topics, $affected_msgs, empty($_POST['enforce_subject']) ? null : array($context['response_prefix'], $target_subject));
    }
    // Send them to the all done page.
    redirectexit('action=mergetopics;sa=done;to=' . $id_topic . ';targetboard=' . $target_board);
}
Exemplo n.º 6
0
/**
 * Take a load of messages from one place and stick them in a topic.
 */
function mergePosts($msgs = array(), $from_topic, $target_topic)
{
    global $context, $smcFunc, $modSettings, $sourcedir;
    //!!! This really needs to be rewritten to take a load of messages from ANY topic, it's also inefficient.
    // Is it an array?
    if (!is_array($msgs)) {
        $msgs = array($msgs);
    }
    // Lets make sure they are int.
    foreach ($msgs as $key => $msg) {
        $msgs[$key] = (int) $msg;
    }
    // Get the source information.
    $request = $smcFunc['db_query']('', '
		SELECT t.id_board, t.id_first_msg, t.num_replies, t.unapproved_posts
		FROM {db_prefix}topics AS t
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
		WHERE t.id_topic = {int:from_topic}', array('from_topic' => $from_topic));
    list($from_board, $from_first_msg, $from_replies, $from_unapproved_posts) = $smcFunc['db_fetch_row']($request);
    $smcFunc['db_free_result']($request);
    // Get some target topic and board stats.
    $request = $smcFunc['db_query']('', '
		SELECT t.id_board, t.id_first_msg, t.num_replies, t.unapproved_posts, b.count_posts
		FROM {db_prefix}topics AS t
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
		WHERE t.id_topic = {int:target_topic}', array('target_topic' => $target_topic));
    list($target_board, $target_first_msg, $target_replies, $target_unapproved_posts, $count_posts) = $smcFunc['db_fetch_row']($request);
    $smcFunc['db_free_result']($request);
    // Lets see if the board that we are returning to has post count enabled.
    if (empty($count_posts)) {
        // Lets get the members that need their post count restored.
        $request = $smcFunc['db_query']('', '
			SELECT id_member
			FROM {db_prefix}messages
			WHERE id_msg IN ({array_int:messages})
				AND approved = {int:is_approved}', array('messages' => $msgs, 'is_approved' => 1));
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            updateMemberData($row['id_member'], array('posts' => '+'));
        }
    }
    // Time to move the messages.
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}messages
		SET
			id_topic = {int:target_topic},
			id_board = {int:target_board},
			icon = {string:icon}
		WHERE id_msg IN({array_int:msgs})', array('target_topic' => $target_topic, 'target_board' => $target_board, 'icon' => $target_board == $modSettings['recycle_board'] ? 'recycled' : 'xx', 'msgs' => $msgs));
    // Fix the id_first_msg and id_last_msg for the target topic.
    $target_topic_data = array('num_replies' => 0, 'unapproved_posts' => 0, 'id_first_msg' => 9999999999);
    $request = $smcFunc['db_query']('', '
		SELECT MIN(id_msg) AS id_first_msg, MAX(id_msg) AS id_last_msg, COUNT(*) AS message_count, approved
		FROM {db_prefix}messages
		WHERE id_topic = {int:target_topic}
		GROUP BY id_topic, approved
		ORDER BY approved ASC
		LIMIT 2', array('target_topic' => $target_topic));
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        if ($row['id_first_msg'] < $target_topic_data['id_first_msg']) {
            $target_topic_data['id_first_msg'] = $row['id_first_msg'];
        }
        $target_topic_data['id_last_msg'] = $row['id_last_msg'];
        if (!$row['approved']) {
            $target_topic_data['unapproved_posts'] = $row['message_count'];
        } else {
            $target_topic_data['num_replies'] = max(0, $row['message_count'] - 1);
        }
    }
    $smcFunc['db_free_result']($request);
    // We have a new post count for the board.
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}boards
		SET
			num_posts = num_posts + {int:diff_replies},
			unapproved_posts = unapproved_posts + {int:diff_unapproved_posts}
		WHERE id_board = {int:target_board}', array('diff_replies' => $target_topic_data['num_replies'] - $target_replies, 'diff_unapproved_posts' => $target_topic_data['unapproved_posts'] - $target_unapproved_posts, 'target_board' => $target_board));
    // In some cases we merged the only post in a topic so the topic data is left behind in the topic table.
    $request = $smcFunc['db_query']('', '
		SELECT id_topic
		FROM {db_prefix}messages
		WHERE id_topic = {int:from_topic}', array('from_topic' => $from_topic));
    // Remove the topic if it doesn't have any messages.
    $topic_exists = true;
    if ($smcFunc['db_num_rows']($request) == 0) {
        removeTopics($from_topic, false, true);
        $topic_exists = false;
    }
    $smcFunc['db_free_result']($request);
    // Recycled topic.
    if ($topic_exists == true) {
        // Fix the id_first_msg and id_last_msg for the source topic.
        $source_topic_data = array('num_replies' => 0, 'unapproved_posts' => 0, 'id_first_msg' => 9999999999);
        $request = $smcFunc['db_query']('', '
			SELECT MIN(id_msg) AS id_first_msg, MAX(id_msg) AS id_last_msg, COUNT(*) AS message_count, approved, subject
			FROM {db_prefix}messages
			WHERE id_topic = {int:from_topic}
			GROUP BY id_topic, approved
			ORDER BY approved ASC
			LIMIT 2', array('from_topic' => $from_topic));
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            if ($row['id_first_msg'] < $source_topic_data['id_first_msg']) {
                $source_topic_data['id_first_msg'] = $row['id_first_msg'];
            }
            $source_topic_data['id_last_msg'] = $row['id_last_msg'];
            if (!$row['approved']) {
                $source_topic_data['unapproved_posts'] = $row['message_count'];
            } else {
                $source_topic_data['num_replies'] = max(0, $row['message_count'] - 1);
            }
        }
        $smcFunc['db_free_result']($request);
        // Update the topic details for the source topic.
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}topics
			SET
				id_first_msg = {int:id_first_msg},
				id_last_msg = {int:id_last_msg},
				num_replies = {int:num_replies},
				unapproved_posts = {int:unapproved_posts}
			WHERE id_topic = {int:from_topic}', array('id_first_msg' => $source_topic_data['id_first_msg'], 'id_last_msg' => $source_topic_data['id_last_msg'], 'num_replies' => $source_topic_data['num_replies'], 'unapproved_posts' => $source_topic_data['unapproved_posts'], 'from_topic' => $from_topic));
        // We have a new post count for the source board.
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}boards
			SET
				num_posts = num_posts + {int:diff_replies},
				unapproved_posts = unapproved_posts + {int:diff_unapproved_posts}
			WHERE id_board = {int:from_board}', array('diff_replies' => $source_topic_data['num_replies'] - $from_replies, 'diff_unapproved_posts' => $source_topic_data['unapproved_posts'] - $from_unapproved_posts, 'from_board' => $from_board));
    }
    // Finally get around to updating the destination topic, now all indexes etc on the source are fixed.
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}topics
		SET
			id_first_msg = {int:id_first_msg},
			id_last_msg = {int:id_last_msg},
			num_replies = {int:num_replies},
			unapproved_posts = {int:unapproved_posts}
		WHERE id_topic = {int:target_topic}', array('id_first_msg' => $target_topic_data['id_first_msg'], 'id_last_msg' => $target_topic_data['id_last_msg'], 'num_replies' => $target_topic_data['num_replies'], 'unapproved_posts' => $target_topic_data['unapproved_posts'], 'target_topic' => $target_topic));
    // Need it to update some stats.
    require_once $sourcedir . '/Subs-Post.php';
    // Update stats.
    updateStats('topic');
    updateStats('message');
    // Subject cache?
    $cache_updates = array();
    if ($target_first_msg != $target_topic_data['id_first_msg']) {
        $cache_updates[] = $target_topic_data['id_first_msg'];
    }
    if (!empty($source_topic_data['id_first_msg']) && $from_first_msg != $source_topic_data['id_first_msg']) {
        $cache_updates[] = $source_topic_data['id_first_msg'];
    }
    if (!empty($cache_updates)) {
        $request = $smcFunc['db_query']('', '
			SELECT id_topic, subject
			FROM {db_prefix}messages
			WHERE id_msg IN ({array_int:first_messages})', array('first_messages' => $cache_updates));
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            updateStats('subject', $row['id_topic'], $row['subject']);
        }
        $smcFunc['db_free_result']($request);
    }
    updateLastMessages(array($from_board, $target_board));
}
Exemplo n.º 7
0
function CopyTopics($original_topic_id, $board_id, $count_posts)
{
    global $txt, $scripturl, $sourcedir, $modSettings, $context, $user_info, $smcFunc;
    // Try to buy some time...
    @set_time_limit(0);
    // Check Topic Exists and get some info for now and later
    $request = $smcFunc['db_query']('', '
		SELECT id_board, id_poll, num_replies
		FROM {db_prefix}topics
		WHERE id_topic = {int:original_topic_id}
		LIMIT 1', array('original_topic_id' => $original_topic_id));
    if ($smcFunc['db_num_rows']($request) == 0) {
        fatal_lang_error('no_topic_id');
    }
    list($original_board_id, $original_poll_id, $num_posts) = $smcFunc['db_fetch_row']($request);
    // Its topic so it has 1 more reply that it states
    $num_posts++;
    $smcFunc['db_free_result']($request);
    // --- Copy Topic Entry ---
    // Query to Copy the Topic
    // The Columns for the table with our new topic and board ids.
    // id_topic is not listed because it is Auto-Incremented
    // id_board is set to our destination board
    // id_first_msg and id_last_msg are set to 0 for now(to prevent key errors if copying into same forum)
    $smcFunc['db_query']('', '
		INSERT INTO {db_prefix}topics (id_board, id_first_msg, id_last_msg, id_member_started, id_member_updated, is_sticky, id_poll, num_replies, num_views, locked)
		SELECT {int:board_id}, 0, 0, id_member_started, id_member_updated, is_sticky, id_poll, num_replies, num_views, locked
		FROM {db_prefix}topics
		WHERE id_topic = {int:original_topic_id}
		LIMIT 1', array('original_topic_id' => $original_topic_id, 'board_id' => $board_id));
    $topic_id = $smcFunc['db_insert_id']('{db_prefix}topics', 'id_topic');
    // --- Copy Messages Entries ---
    // Query to Copy EVERY post in the topic (Potentially could be a beast of a query)
    // The Columns for the table with our new topic and board ids.
    // id_msg is not listed because it is Auto-Incremented
    // id_msg_modified is made to the old msg id temporarily.
    $smcFunc['db_query']('', '
		INSERT INTO {db_prefix}messages (id_topic, id_board, id_msg_modified, id_member, poster_time, subject, poster_name, poster_email, poster_ip, modified_name, body, icon, approved)
		SELECT {int:topic_id}, {int:board_id}, id_msg, id_member, poster_time, subject, poster_name, poster_email, poster_ip, modified_name, body, icon, approved
		FROM {db_prefix}messages
		WHERE id_topic = {int:original_topic_id}
		ORDER BY id_msg ASC', array('original_topic_id' => $original_topic_id, 'topic_id' => $topic_id, 'board_id' => $board_id));
    // --- Log Search Subject Cache (for searching) ---
    // Standard
    // Query to Copy EVERY matching row
    // The Columns for the table with our new topic id instead
    $smcFunc['db_query']('', '
		INSERT INTO {db_prefix}log_search_subjects (word, id_topic)
		SELECT word, {int:topic_id}
		FROM {db_prefix}log_search_subjects
		WHERE id_topic = {int:original_topic_id}', array('original_topic_id' => $original_topic_id, 'topic_id' => $topic_id));
    // Custom Search Index?
    if (!empty($modSettings['search_custom_index_config'])) {
        // Query to Copy EVERY matching row
        // The Columns for the table with our new msg id instead
        $smcFunc['db_query']('', '
			INSERT INTO {db_prefix}log_search_words (id_word, id_msg)
			SELECT w.id_word, m.id_msg
			FROM {db_prefix}log_search_words as w
				LEFT JOIN {db_prefix}messages as m ON (w.id_msg = m.id_msg_modified)
			WHERE m.id_topic = {int:topic_id}', array('topic_id' => $topic_id));
    }
    /* Disabled v1.2 - Causing duplicate key issues
    	// --- Log_Search_Results ---
    
    	// Query to Copy EVERY matching row
    	// Include this new topic in existing search results
    	// The Columns for the table with our new msg id and topic id instead
    	$smcFunc['db_query']('', '
    		INSERT INTO {db_prefix}log_search_results (id_search, id_topic, id_msg, relevance, num_matches)
    		SELECT r.id_search, {int:topic_id}, m.id_msg, r.relevance, r.num_matches
    		FROM {db_prefix}log_search_results as r
    			LEFT JOIN {db_prefix}messages as m ON (r.id_msg = m.id_msg_modified)
    		WHERE m.id_topic = {int:topic_id}',
    		array(
    			'topic_id' => $topic_id,
    		)
    	);
    	*/
    // --- Copy Attachments ---
    // * Only those less than 1mb in size, larger files may crash your server
    // We need to know where this thing is going.
    if (!empty($modSettings['currentAttachmentUploadDir'])) {
        if (!is_array($modSettings['attachmentUploadDir'])) {
            $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
        }
        // Just use the current path for temp files.
        $attach_dir = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
        $id_folder = $modSettings['currentAttachmentUploadDir'];
    } else {
        $attach_dir = $modSettings['attachmentUploadDir'];
        $id_folder = 1;
    }
    $doattachments = 1;
    // First make sure the attachment dir is writable.
    if (!is_writable($attach_dir)) {
        // Try to fix it.
        @chmod($attach_dir, 0777);
        // Guess that didn't work :/?
        if (!is_writable($attach_dir)) {
            $doattachments = 0;
        }
    }
    // Check how we are on directory filesize?
    if ($doattachments && !empty($modSettings['attachmentDirSizeLimit'])) {
        $doattachments = attachmentDirectorySizeCheck($attach_dir);
    }
    // Right try to go ahead with the attachments.
    if (!empty($doattachments)) {
        // id_attach is not listed because it is Auto-Incremented
        // Matching the id_msg_modified and switching it for the new id_msg
        // id_folder is new in 2.0 (for multiple attachment directory support), will be changed later once its been copied
        $smcFunc['db_query']('', '
			INSERT INTO {db_prefix}attachments (fileext, mime_type, approved, id_folder, id_thumb, id_msg, id_member, attachment_type, filename, size, downloads, width, height)
			SELECT fileext, a.mime_type, a.approved, a.id_folder, a.id_attach, m.id_msg, a.id_member, a.attachment_type, a.filename, a.size, a.downloads, a.width, a.height
			FROM {db_prefix}attachments as a
				LEFT JOIN {db_prefix}messages as m ON (a.id_msg = m.id_msg_modified)
			WHERE m.id_topic = {int:topic_id}
				AND size < 1048600
			ORDER BY id_attach ASC', array('topic_id' => $topic_id));
        // Grab and store the new vs old attachment ids in a array (we temporarily stored the old ID in the id_thumb column)
        $request = $smcFunc['db_query']('', '
			SELECT a.id_attach, a.id_thumb
			FROM {db_prefix}attachments as a
				LEFT JOIN {db_prefix}messages as m ON (a.id_msg = m.id_msg)
			WHERE m.id_topic = {int:topic_id}
			ORDER BY id_attach ASC', array('topic_id' => $topic_id));
        // Did it copy any attachment entries in the db?
        if ($smcFunc['db_num_rows']($request) != 0) {
            $ids = array();
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                $ids[$row['id_attach']] = $row['id_thumb'];
            }
            // Tidy up
            $smcFunc['db_free_result']($request);
            unset($row);
            // Re-Associate Thumbs with our new attachment and thumb ids
            // Grab all the information about the attachments so we can re-associate them to the correct
            $request = $smcFunc['db_query']('', '
				SELECT a.id_attach as attach_id, IFNULL(a3.id_attach, 0) as thumb_id
				FROM {db_prefix}attachments as a
					LEFT JOIN {db_prefix}messages as m ON (a.id_msg = m.id_msg)
					LEFT JOIN {db_prefix}attachments as a2 ON (a.id_thumb = a2.id_attach)
					LEFT JOIN {db_prefix}attachments as a3 ON (a2.id_thumb = a3.id_thumb AND m.id_msg = a3.id_msg)
				WHERE m.id_topic = {int:topic_id}
					AND a2.id_attach != 0
				', array('topic_id' => $topic_id));
            $change = array();
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                $change[$row['attach_id']] = $row['thumb_id'];
            }
            foreach ($change as $a => $b) {
                $smcFunc['db_query']('', '
					UPDATE {db_prefix}attachments
					SET id_thumb = {int:thumb_id}
					WHERE id_attach = {int:attach_id}
						AND id_attach != 0', array('attach_id' => $a, 'thumb_id' => (int) $b));
            }
            unset($a, $b, $change, $row);
            // Grab all the information about the attachments (including thumbs) attached to this topic
            $request = $smcFunc['db_query']('', '
				SELECT 	a.id_attach as file_id, a.filename as filename, a.size as filesize,
						a.id_thumb as thumb_id, a2.filename as thumbname, a2.size as thumbsize,
						a.id_folder as folder, a2.id_folder as thumbfolder
				FROM {db_prefix}attachments as a
					LEFT JOIN {db_prefix}messages as m ON (a.id_msg = m.id_msg)
					LEFT JOIN {db_prefix}attachments as a2 ON (a.id_thumb = a2.id_attach)
				WHERE m.id_topic = {int:topic_id}
					AND	a.attachment_type != 3
				ORDER BY a.id_attach ASC', array('topic_id' => $topic_id));
            // Attachments found
            if ($smcFunc['db_num_rows']($request) != 0) {
                $attachments = array();
                // For each attachment, we will try to copy the file
                while ($row = $smcFunc['db_fetch_assoc']($request)) {
                    $row['original_id'] = $ids[$row['file_id']];
                    $row['original_thumb_id'] = empty($row['thumb_id']) ? 0 : $ids[$row['thumb_id']];
                    $attachments[] = $row;
                }
                // Tidy up
                $smcFunc['db_free_result']($request);
                unset($ids, $row);
                foreach ($attachments as $row) {
                    // Is there enough space for the copied attachment + thumb
                    if (empty($modSettings['attachmentDirSizeLimit']) || $doattachments + (int) $row['filesize'] + (int) $row['thumbsize'] < $modSettings['attachmentDirSizeLimit'] * 1024) {
                        // Copy the attachment
                        if ($filename = copyAttachment($row, $attach_dir)) {
                            // Successly copied the attachment
                            // Update the attachment db entry with the new filename (only if new filename was generated);
                            $smcFunc['db_query']('', '
								UPDATE {db_prefix}attachments
								SET filename = {string:filename}, id_folder = {int:id_folder}
								WHERE id_attach = {int:file_id}
								', array('file_id' => $row['file_id'], 'filename' => $filename, 'id_folder' => $id_folder));
                            // Increase the size
                            $doattachments = $doattachments + (int) $row['filesize'] + (int) $row['thumbsize'];
                            // If theres a thumb, rename that aswell
                            if (!empty($row['thumb_id'])) {
                                $smcFunc['db_query']('', '
									UPDATE {db_prefix}attachments
									SET filename = {string:thumbname}, id_folder = {int:id_folder}
									WHERE id_attach = {int:thumb_id}
									', array('thumb_id' => $row['thumb_id'], 'thumbname' => $filename . '_thumb', 'id_folder' => $id_folder));
                            }
                        } else {
                            // Copying the attachment failed, so delete the db entries
                            $smcFunc['db_query']('', '
								DELETE FROM {db_prefix}attachments
								WHERE id_attach = {int:file_id}
									OR id_attach = {int:thumb_id}
								', array('file_id' => $row['file_id'], 'thumb_id' => $row['thumb_id']));
                        }
                    } else {
                        // Ran out of space or error, so delete the db entries
                        $smcFunc['db_query']('', '
							DELETE FROM {db_prefix}attachments
							WHERE id_attach = {int:file_id}
								OR id_attach = {int:thumb_id}
							', array('file_id' => $row['file_id'], 'thumb_id' => $row['thumb_id']));
                    }
                    // Tidy up
                    unset($row);
                }
                // Tidy up
                unset($attachments, $ids, $row);
            }
        }
    }
    //  Fix Attachment Icon, if icon is set to clip and no attachments were copied.
    $request = $smcFunc['db_query']('', '
		SELECT count(a.id_attach) as attachments, m.icon, m.id_msg
		FROM {db_prefix}messages as m
			LEFT JOIN {db_prefix}attachments as a ON (m.id_msg = a.id_msg)
		WHERE m.id_topic = {int:topic_id}
		GROUP BY a.id_attach
		', array('topic_id' => $topic_id));
    $fix = array();
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        if ($row['attachments'] == 0 && $row['icon'] == 'clip') {
            $fix[] = $row['id_msg'];
        }
    }
    if (!empty($fix)) {
        // So change it back to default
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}messages
			SET icon = {string:default_icon}
			WHERE id_topic = {int:topic_id}
				AND id_msg IN ({array_int:id_msgs})
				AND icon = {string:clip}
			', array('topic_id' => $topic_id, 'id_msgs' => $fix, 'default_icon' => 'xx', 'clip' => 'clip'));
    }
    // Tidy up
    unset($fix, $row);
    // --- Fix some stats and logs ---
    // Fix id_msg_modified to the New id_msg
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}messages
		SET id_msg_modified = id_msg
		WHERE id_topic = {int:topic_id}', array('topic_id' => $topic_id));
    // Grab First & Last Message Id
    $request = $smcFunc['db_query']('', '
		SELECT max(id_msg) as last, min(id_msg) as first
		FROM {db_prefix}messages
		WHERE id_topic = {int:topic_id}', array('topic_id' => $topic_id));
    $row = $smcFunc['db_fetch_assoc']($request);
    // Update the topic info with that info
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}topics
		SET id_first_msg = {int:first}, id_last_msg = {int:last}
		WHERE id_topic = {int:topic_id}', array('topic_id' => $topic_id, 'first' => $row['first'], 'last' => $row['last']));
    // Update log topics (for this user only)
    $smcFunc['db_query']('', '
		REPLACE
		INTO {db_prefix}log_topics
			(id_topic, id_member, id_msg)
		VALUES ({int:topic_id}, {int:user_id}, {int:last})
		', array('topic_id' => $topic_id, 'last' => $row['last'], 'user_id' => $context['user']['id']));
    // Update log boards (for this user only)
    $smcFunc['db_query']('', '
		REPLACE
		INTO {db_prefix}log_boards
			(id_board, id_member, id_msg)
		VALUES ({int:board_id}, {int:user_id}, {int:last})
		', array('board_id' => $board_id, 'last' => $row['last'], 'user_id' => $context['user']['id']));
    require_once $sourcedir . '/Subs-Post.php';
    updateLastMessages($board_id, $row['last']);
    // --- Fix Post Counts ---
    // Posts in the board we're copying the topic to count, so we need to get the figures for each
    if ($count_posts) {
        // Increase the stats for the board
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}boards
			SET num_posts = num_posts + {int:num_posts}, num_topics = num_topics + 1
			WHERE id_board = {int:board_id}
			', array('board_id' => $board_id, 'num_posts' => $num_posts));
        // How many posts have been made by each user in the copied topic?
        $request = $smcFunc['db_query']('', '
			SELECT count(*) as increase, id_member
			FROM {db_prefix}messages
			WHERE id_topic = {int:topic_id}
				AND id_member > 0
			GROUP BY id_member
			', array('topic_id' => $topic_id));
        // Any members to update (non-guests);
        if ($smcFunc['db_num_rows']($request) != 0) {
            $members = $increase = array();
            // Prepare the information in arrays for easy update
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                $increase[$row['id_member']] = $row['increase'];
                // Store the member ids, as we will need to Update PostGroups
                if (!in_array($row['id_member'], $members)) {
                    $members[] = $row['id_member'];
                }
            }
            // Update each users postcount accordingly.  Could add significant number of queries for large topics.
            foreach ($increase as $a => $b) {
                $smcFunc['db_query']('', '
					UPDATE {db_prefix}members
					SET posts = posts + {int:posts}
					WHERE id_member = {int:id_member}
					', array('id_member' => $a, 'posts' => $b));
            }
            unset($increase, $a, $b);
            // Update PostGroups for member who's postcounts have been altered.
            updateStats('postgroups', $members);
        }
    }
    $_SESSION['last_read_topic'] = $original_topic_id;
    // --- Copy Poll ---
    // Is it a poll?
    if ($original_poll_id > 0) {
        // Copy the poll
        // id_poll is not listed because it is Auto-Incremented
        // The rest are the same
        $smcFunc['db_query']('', '
			INSERT INTO {db_prefix}polls (question, voting_locked, max_votes, expire_time, hide_results, change_vote, id_member, poster_name)
			SELECT question, voting_locked, max_votes, expire_time, hide_results, change_vote, id_member, poster_name
			FROM {db_prefix}polls
			WHERE id_poll = {int:original_poll_id}
			', array('original_poll_id' => $original_poll_id));
        // Save the new poll id
        $poll_id = $smcFunc['db_insert_id']('{db_prefix}polls', 'id_poll');
        // Update the topic info with the poll id
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}topics
			SET id_poll = {int:poll_id}
			WHERE id_topic = {int:topic_id}
			', array('poll_id' => $poll_id, 'topic_id' => $topic_id));
        // --- Copy Poll Choices ---
        // Query to Select & Copy ALL the poll choices in one query.
        // id_poll is set to our new poll id
        $smcFunc['db_query']('', '
			INSERT INTO {db_prefix}poll_choices (id_poll, id_choice, label, votes)
			SELECT {int:poll_id}, id_choice, label, votes
			FROM {db_prefix}poll_choices
			WHERE id_poll = {int:original_poll_id}
			ORDER BY id_choice ASC
			', array('poll_id' => $poll_id, 'original_poll_id' => $original_poll_id));
        // --- Copy Log Polls ---
        // Query to Select & Copy the log polls in one query.  (depending on the no. of voters, it could be heavy)
        // id_poll is set to our new poll id
        $smcFunc['db_query']('', '
			INSERT INTO {db_prefix}log_polls(id_poll, id_member, id_choice)
			SELECT {int:poll_id}, id_member, id_choice
			FROM {db_prefix}log_polls
			WHERE id_poll = {int:original_poll_id}
			', array('poll_id' => $poll_id, 'original_poll_id' => $original_poll_id));
    }
    // --- Calender Events ---
    // Query to Copy each calendar entry related to this topic
    // id_event is not listed as its auto-incremented
    // id_topic and id_board are set to our new poll id
    $smcFunc['db_query']('', '
		INSERT INTO {db_prefix}calendar (start_date, end_date, id_board, id_topic, title, id_member)
		SELECT start_date, end_date, {int:board_id}, {int:topic_id}, title, id_member
		FROM {db_prefix}calendar
		WHERE id_topic = {int:original_topic_id}
		ORDER BY id_event ASC
		', array('topic_id' => $topic_id, 'board_id' => $board_id, 'original_topic_id' => $original_topic_id));
    updateStats('topic');
    updateStats('message');
    updateSettings(array('calendar_updated' => time()));
}
Exemplo n.º 8
0
function CopyTopics($original_topic_id, $board_id, $countPosts)
{
    global $db_prefix, $txt, $scripturl, $sourcedir, $modSettings, $context, $user_info;
    // Try to buy some time...
    @set_time_limit(0);
    // Check Topic Exists and get some info for now and later
    $request = db_query("\n\t\tSELECT ID_BOARD, ID_POLL, numReplies\n\t\tFROM {$db_prefix}topics\n\t\tWHERE ID_TOPIC = {$original_topic_id}\n\t\tLIMIT 1\n\t\t", __FILE__, __LINE__);
    if (mysql_num_rows($request) == 0) {
        fatal_lang_error('smf263');
    }
    list($original_board_id, $original_poll_id, $numPosts) = mysql_fetch_row($request);
    // Its topic so it has 1 more reply that it states
    $numPosts++;
    mysql_free_result($request);
    // --- Copy Topic Entry ---
    // The Columns for the table with our new topic and board ids.
    // ID_TOPIC is not listed because it is Auto-Incremented
    // ID_BOARD is set to our destination board
    // ID_FIRST_MSG and ID_LAST_MSG are set to 0 for now(to prevent key errors if copying into same forum)
    $insert = "ID_BOARD, ID_FIRST_MSG, ID_LAST_MSG, ID_MEMBER_STARTED, ID_MEMBER_UPDATED, isSticky, ID_POLL, numReplies, numViews, locked";
    $select = "'{$board_id}', 0, 0, ID_MEMBER_STARTED, ID_MEMBER_UPDATED, isSticky, ID_POLL, numReplies, numViews, locked";
    // Query to Copy the Topic
    db_query("\n\t\tINSERT INTO {$db_prefix}topics (" . $insert . ")\n\t\tSELECT " . $select . "\n\t\tFROM {$db_prefix}topics\n\t\tWHERE ID_TOPIC = '" . $original_topic_id . "'\n\t\tLIMIT 1\n\t", __FILE__, __LINE__);
    $topic_id = db_insert_id();
    // Tidy up
    unset($insert, $select);
    // --- Copy Messages Entries ---
    // The Columns for the table with our new topic and board ids.
    // ID_MSG is not listed because it is Auto-Incremented
    // ID_MSG_MODIFIED is made to the old msg id temporarily.
    $insert = "ID_TOPIC, ID_BOARD, ID_MSG_MODIFIED, ID_MEMBER, posterTime, subject, posterName, posterEmail, posterIP, modifiedName, body, icon";
    $select = "'{$topic_id}', '{$board_id}', ID_MSG, ID_MEMBER, posterTime, subject, posterName, posterEmail, posterIP, modifiedName, body, icon";
    // Query to Copy EVERY post in the topic (Potentially could be a beast of a query)
    db_query("\n\t\tINSERT INTO {$db_prefix}messages (" . $insert . ")\n\t\tSELECT " . $select . "\n\t\tFROM {$db_prefix}messages\n\t\tWHERE ID_TOPIC = " . $original_topic_id . "\n\t\tORDER BY ID_MSG ASC\n\t", __FILE__, __LINE__);
    // Tidy up
    unset($insert, $select);
    // --- Log Search Subject Cache (for searching) ---
    // Standard
    // The Columns for the table with our new topic id instead
    $insert = "word, ID_TOPIC";
    $select = "word, '{$topic_id}'";
    // Query to Copy EVERY matching row
    db_query("\n\t\tINSERT INTO {$db_prefix}log_search_subjects (" . $insert . ")\n\t\tSELECT " . $select . "\n\t\tFROM {$db_prefix}log_search_subjects\n\t\tWHERE ID_TOPIC = " . $original_topic_id . "\n\t", __FILE__, __LINE__);
    // Tidy up
    unset($insert, $select);
    // Custom Search Index?
    if (!empty($modSettings['search_custom_index_config'])) {
        // The Columns for the table with our new msg id instead
        $insert = "ID_WORD, ID_MSG";
        $select = "w.ID_WORD, m.ID_MSG";
        // Query to Copy EVERY matching row
        db_query("\n\t\t\tINSERT INTO {$db_prefix}log_search_words (" . $insert . ")\n\t\t\tSELECT " . $select . "\n\t\t\tFROM {$db_prefix}log_search_words as w\n\t\t\t\tLEFT JOIN {$db_prefix}messages as m ON (w.ID_MSG = m.ID_MSG_MODIFIED)\n\t\t\tWHERE m.ID_TOPIC = " . $topic_id . "\n\t\t", __FILE__, __LINE__);
        // Tidy up
        unset($insert, $select);
    }
    /* Disabled v1.2 - Causing duplicate key issues
    	// --- Log_Search_Results ---
    	// Include this new topic in existing search results
    	// The Columns for the table with our new msg id and topic id instead	
    	$insert = "ID_SEARCH, ID_TOPIC, ID_MSG, relevance, num_matches";
    	$select = "r.ID_SEARCH, '$topic_id', m.ID_MSG, r.relevance, r.num_matches";
    	
    	// Query to Copy EVERY matching row
    	db_query("
    		INSERT INTO {$db_prefix}log_search_results (". $insert .")
    		SELECT ". $select ."
    		FROM {$db_prefix}log_search_results as r
    			LEFT JOIN {$db_prefix}messages as m ON (r.ID_MSG = m.ID_MSG_MODIFIED)
    		WHERE m.ID_TOPIC = ".$topic_id."
    	", __FILE__, __LINE__);
    	
    	// Tidy up
    	unset($insert,$select);
    	*/
    // --- Copy Attachments ---
    // * Only those less than 1mb in size, larger files may crash your server
    $doattachments = 1;
    // First make sure the attachment dir is writable.
    if (!is_writable($modSettings['attachmentUploadDir'])) {
        // Try to fix it.
        @chmod($modSettings['attachmentUploadDir'], 0777);
        // Guess that didn't work :/?
        if (!is_writable($modSettings['attachmentUploadDir'])) {
            $doattachments = 0;
        }
    }
    // Check how we are on directory filesize?
    if ($doattachments && !empty($modSettings['attachmentDirSizeLimit'])) {
        $doattachments = attachmentDirectorySizeCheck();
    }
    // Right try to go ahead with the attachments.
    if (!empty($doattachments)) {
        // The Columns for the table
        // ID_ATTACH is not listed because it is Auto-Incremented
        // Matching the ID_MSG_MODIFIED and switching it for the new ID_MSG
        $insert = "ID_THUMB, ID_MSG, ID_MEMBER, attachmentType, filename, size, downloads, width, height";
        $select = "a.ID_ATTACH, m.ID_MSG, a.ID_MEMBER, a.attachmentType, a.filename, a.size, a.downloads, a.width, a.height";
        db_query("\n\t\t\tINSERT INTO {$db_prefix}attachments (" . $insert . ")\n\t\t\tSELECT " . $select . "\n\t\t\tFROM {$db_prefix}attachments as a\n\t\t\t\tLEFT JOIN {$db_prefix}messages as m ON (a.ID_MSG = m.ID_MSG_MODIFIED)\n\t\t\tWHERE m.ID_TOPIC = " . $topic_id . "\n\t\t\t\tAND size < 1048600\n\t\t\tORDER BY ID_ATTACH ASC\n\t\t", __FILE__, __LINE__);
        // Tidy up
        unset($insert, $select);
        // Grab and store the new vs old attachment ids in a array (we temporarily stored the old ID in the ID_THUMB column)
        $request = db_query("\n\t\t\tSELECT a.ID_ATTACH, a.ID_THUMB\n\t\t\tFROM {$db_prefix}attachments as a\n\t\t\t\tLEFT JOIN {$db_prefix}messages as m ON (a.ID_MSG = m.ID_MSG)\n\t\t\tWHERE m.ID_TOPIC = " . $topic_id . "\n\t\t\tORDER BY ID_ATTACH ASC\n\t\t", __FILE__, __LINE__);
        // Did it copy any attachment entries in the db?
        if (mysql_num_rows($request) != 0) {
            $ids = array();
            while ($row = mysql_fetch_assoc($request)) {
                $ids[$row['ID_ATTACH']] = $row['ID_THUMB'];
            }
            // Tidy up
            mysql_free_result($request);
            unset($row);
            // Re-Associate Thumbs with our new attachment and thumb ids
            db_query("\n\t\t\t\tUPDATE {$db_prefix}attachments as a\n\t\t\t\t\tLEFT JOIN {$db_prefix}messages as m ON (a.ID_MSG = m.ID_MSG)\n\t\t\t\t\tLEFT JOIN {$db_prefix}attachments as a2 ON (a.ID_THUMB = a2.ID_ATTACH)\n\t\t\t\t\tLEFT JOIN {$db_prefix}attachments as a3 ON (a2.ID_THUMB = a3.ID_THUMB AND m.ID_MSG = a3.ID_MSG)\n\t\t\t\tSET a.ID_THUMB = a3.ID_ATTACH\n\t\t\t\tWHERE m.ID_TOPIC = " . $topic_id . "\n\t\t\t\t\tAND a2.ID_ATTACH != 0\n\t\t\t", __FILE__, __LINE__);
            // Grab all the information about the attachments (including thumbs) attached to this topic
            $request = db_query("\n\t\t\t\tSELECT \ta.ID_ATTACH as file_id, a.filename as filename, a.size as filesize,\n\t\t\t\t\t\ta.ID_THUMB as thumb_id, a2.filename as thumbname, a2.size as thumbsize\n\t\t\t\tFROM {$db_prefix}attachments as a\n\t\t\t\t\tLEFT JOIN {$db_prefix}messages as m ON (a.ID_MSG = m.ID_MSG)\n\t\t\t\t\tLEFT JOIN {$db_prefix}attachments as a2 ON (a.ID_THUMB = a2.ID_ATTACH)\n\t\t\t\tWHERE m.ID_TOPIC = " . $topic_id . "\n\t\t\t\t\tAND\ta.attachmentType != 3\n\t\t\t\tORDER BY a.ID_ATTACH ASC\n\t\t\t", __FILE__, __LINE__);
            // Attachments found
            if (mysql_num_rows($request) != 0) {
                $attachments = array();
                // For each attachment, we will try to copy the file
                while ($row = mysql_fetch_assoc($request)) {
                    $row['original_id'] = $ids[$row['file_id']];
                    $row['original_thumb_id'] = empty($row['thumb_id']) ? 0 : $ids[$row['thumb_id']];
                    $attachments[] = $row;
                }
                // Tidy up
                mysql_free_result($request);
                unset($ids, $row);
                foreach ($attachments as $row) {
                    // Is there enough space for the copied attachment + thumb
                    if (empty($modSettings['attachmentDirSizeLimit']) || $doattachments + (int) $row['filesize'] + (int) $row['thumbsize'] < $modSettings['attachmentDirSizeLimit'] * 1024) {
                        // Copy the attachment
                        if ($filename = copyAttachment($row)) {
                            // Successly copied the attachment
                            // Update the attachment db entry with the new filename (only if new filename was generated);
                            db_query("\n\t\t\t\t\t\t\t\tUPDATE {$db_prefix}attachments\n\t\t\t\t\t\t\t\tSET filename = '{$filename}'\n\t\t\t\t\t\t\t\tWHERE ID_ATTACH = " . (int) $row['file_id'] . "\n\t\t\t\t\t\t\t", __FILE__, __LINE__);
                            // Increase the size
                            $doattachments = $doattachments + (int) $row['filesize'] + (int) $row['thumbsize'];
                            // If theres a thumb, rename that aswell
                            if (!empty($row['thumb_id'])) {
                                db_query("\n\t\t\t\t\t\t\t\t\tUPDATE {$db_prefix}attachments\n\t\t\t\t\t\t\t\t\tSET filename = '" . $filename . "_thumb'\n\t\t\t\t\t\t\t\t\tWHERE ID_ATTACH = " . (int) $row['thumb_id'] . "\n\t\t\t\t\t\t\t\t", __FILE__, __LINE__);
                            }
                        } else {
                            // Copying the attachment failed, so delete the db entries
                            db_query("\n\t\t\t\t\t\t\t\tDELETE FROM {$db_prefix}attachments\n\t\t\t\t\t\t\t\tWHERE ID_ATTACH = " . (int) $row['file_id'] . "\n\t\t\t\t\t\t\t\t\tOR ID_ATTACH = " . (int) $row['thumb_id'] . "\n\t\t\t\t\t\t\t", __FILE__, __LINE__);
                        }
                    } else {
                        // Ran out of space or error, so delete the db entries
                        db_query("\n\t\t\t\t\t\t\tDELETE FROM {$db_prefix}attachments\n\t\t\t\t\t\t\tWHERE ID_ATTACH = " . (int) $row['file_id'] . "\n\t\t\t\t\t\t\t\tOR ID_ATTACH = " . (int) $row['thumb_id'] . "\n\t\t\t\t\t\t", __FILE__, __LINE__);
                    }
                    // Tidy up
                    unset($row);
                }
                // Tidy up
                unset($attachments, $ids, $row);
            }
        }
    }
    // --- Fix some stats and logs ---
    // Fix ID_MSG_MODIFIED to the New ID_MSG
    db_query("\n\t\tUPDATE {$db_prefix}messages\n\t\tSET ID_MSG_MODIFIED = ID_MSG\n\t\tWHERE ID_TOPIC = " . $topic_id . "\n\t", __FILE__, __LINE__);
    // Grab First & Last Message Id
    $request = db_query("\n\t\tSELECT max(ID_MSG) as last, min(ID_MSG) as first\n\t\tFROM {$db_prefix}messages\n\t\tWHERE ID_TOPIC = " . $topic_id . "\n\t", __FILE__, __LINE__);
    $row = mysql_fetch_assoc($request);
    // Update the topic info with that info
    db_query("\n\t\tUPDATE {$db_prefix}topics\n\t\tSET ID_FIRST_MSG = " . $row['first'] . ", ID_LAST_MSG = " . $row['last'] . "\n\t\tWHERE ID_TOPIC = " . $topic_id . "\n\t", __FILE__, __LINE__);
    // Update log topics (for this user only)
    db_query("\n\t\tREPLACE\n\t\tINTO {$db_prefix}log_topics\n\t\t\t(ID_TOPIC, ID_MEMBER, ID_MSG)\n\t\tVALUES (" . $topic_id . ", " . $context['user']['id'] . ", " . $row['last'] . ")\n\t\t", __FILE__, __LINE__);
    // Update log boards (for this user only)
    db_query("\n\t\tREPLACE\n\t\tINTO {$db_prefix}log_boards\n\t\t\t(ID_BOARD, ID_MEMBER, ID_MSG)\n\t\tVALUES (" . $board_id . ", " . $context['user']['id'] . ", " . $row['last'] . ")\n\t\t", __FILE__, __LINE__);
    require_once $sourcedir . '/Subs-Post.php';
    updateLastMessages($board_id, $row['last']);
    // --- Fix Post Counts ---
    // Posts in the board we're copying the topic to count, so we need to get the figures for each
    if ($countPosts) {
        // Increase the stats for the board
        db_query("\n\t\t\tUPDATE {$db_prefix}boards\n\t\t\tSET numPosts = numPosts + " . $numPosts . ", numTopics = numTopics + 1\n\t\t\tWHERE ID_BOARD = " . $board_id . "\n\t\t", __FILE__, __LINE__);
        // How many posts have been made by each user in the copied topic?
        $request = db_query("\n\t\t\tSELECT count(*) as increase, ID_MEMBER\n\t\t\tFROM {$db_prefix}messages\n\t\t\tWHERE ID_TOPIC = " . $topic_id . "\n\t\t\t\tAND ID_MEMBER > 0\n\t\t\tGROUP BY ID_MEMBER\n\t\t", __FILE__, __LINE__);
        // Any members to update (non-guests);
        if (mysql_num_rows($request) != 0) {
            $members = $increase = array();
            // Prepare the information in arrays for easy update
            while ($row = mysql_fetch_assoc($request)) {
                $increase[$row['ID_MEMBER']] = $row['increase'];
                // Store the member ids, as we will need to Update PostGroups
                if (!in_array($row['ID_MEMBER'], $members)) {
                    $members[] = $row['ID_MEMBER'];
                }
            }
            // Update each users postcount accordingly.  Could add significant number of queries for large topics.
            foreach ($increase as $a => $b) {
                db_query("\n\t\t\t\t\tUPDATE {$db_prefix}members\n\t\t\t\t\tSET posts = posts + " . $b . "\n\t\t\t\t\tWHERE ID_MEMBER = " . $a . "\n\t\t\t\t", __FILE__, __LINE__);
            }
            unset($increase, $a, $b);
            // Update PostGroups for member who's postcounts have been altered.
            updateStats('postgroups', 'ID_MEMBER IN (' . implode(', ', $members) . ')');
        }
    }
    $_SESSION['last_read_topic'] = $original_topic_id;
    // --- Copy Poll ---
    // Is it a poll?
    if ($original_poll_id > 0) {
        // The Columns for the table
        // ID_POLL is not listed because it is Auto-Incremented
        // The rest are the same
        $select = $insert = "question, votingLocked, maxVotes, expireTime, hideResults, changeVote, ID_MEMBER, posterName";
        // Copy the poll
        db_query("\n\t\t\tINSERT INTO {$db_prefix}polls (" . $insert . ")\n\t\t\tSELECT " . $select . "\n\t\t\tFROM {$db_prefix}polls\n\t\t\tWHERE ID_POLL = '" . $original_poll_id . "'\n\t\t", __FILE__, __LINE__);
        // Save the new poll id
        $poll_id = db_insert_id();
        // Update the topic info with the poll id
        db_query("\n\t\t\tUPDATE {$db_prefix}topics\n\t\t\tSET ID_POLL = " . $poll_id . "\n\t\t\tWHERE ID_TOPIC = " . $topic_id . "\n\t\t", __FILE__, __LINE__);
        // Tidy up
        unset($insert, $select);
        // --- Copy Poll Choices ---
        // The Columns for the table
        // ID_POLL is set to our new poll id
        $insert = "ID_POLL, ID_CHOICE, label, votes";
        $select = "'{$poll_id}', ID_CHOICE, label, votes";
        // Query to Select & Copy ALL the poll choices in one query.
        db_query("\n\t\t\tINSERT INTO {$db_prefix}poll_choices (" . $insert . ")\n\t\t\tSELECT " . $select . "\n\t\t\tFROM {$db_prefix}poll_choices\n\t\t\tWHERE ID_POLL = " . $original_poll_id . "\n\t\t\tORDER BY ID_CHOICE ASC\n\t\t", __FILE__, __LINE__);
        // Tidy up
        unset($insert, $select);
        // --- Copy Log Polls ---
        // The Columns for the table
        // ID_POLL is set to our new poll id
        $insert = "ID_POLL, ID_MEMBER, ID_CHOICE";
        $select = "'{$poll_id}', ID_MEMBER, ID_CHOICE";
        // Query to Select & Copy the log polls in one query.  (depending on the no. of voters, it could be heavy)
        db_query("\n\t\t\tINSERT INTO {$db_prefix}log_polls(" . $insert . ")\n\t\t\tSELECT " . $select . "\n\t\t\tFROM {$db_prefix}log_polls\n\t\t\tWHERE ID_POLL = " . $original_poll_id . "\n\t\t", __FILE__, __LINE__);
        // Tidy up
        unset($insert, $select);
    }
    // --- Calender Events ---
    // The Columns for the table
    // ID_EVENT is not listed as its auto-incremented
    // ID_TOPIC and ID_BOARD are set to our new poll id
    $insert = "startDate, endDate, ID_BOARD, ID_TOPIC, title, ID_MEMBER";
    $select = "startDate, endDate, '{$board_id}', '{$topic_id}', title, ID_MEMBER";
    // Query to Copy each calendar entry related to this topic
    db_query("\n\t\tINSERT INTO {$db_prefix}calendar (" . $insert . ")\n\t\tSELECT " . $select . "\n\t\tFROM {$db_prefix}calendar\n\t\tWHERE ID_TOPIC = " . $original_topic_id . "\n\t\tORDER BY ID_EVENT ASC\n\t", __FILE__, __LINE__);
    updateStats('topic');
    updateStats('message');
    updateStats('calendar');
}
/**
 *	Handles moving a topic into the helpdesk.
 *
 *	After checking permissions, and so on, begin to actually move posts.
 *
 *	Broadly this is done using {@link shd_create_ticket_post()}, which has hooks specifically to deal with post modification times (written in specifically to ease this function's workload)
 *
 *	Operations:
 *	- get the topic information (and checking topic access permission in the process)
 *	- identify the status of the topic (new/with staff/with user)
 *	- create the new ticket from these details
 *	- assuming there are replies, query for them
 *	- step through and repost
 *	- send the notification PM if we're doing that
 *	- update the attachments table
 *	- update the action log
 *	- remove the topic from the forum
 *
 *	@see shd_topictoticket()
 *	@since 1.0
*/
function shd_topictoticket2()
{
    global $smcFunc, $context, $txt, $modSettings, $scripturl, $sourcedir;
    checkSession();
    checkSubmitOnce('check');
    $_REQUEST['dept'] = isset($_REQUEST['dept']) ? (int) $_REQUEST['dept'] : 0;
    if (empty($_REQUEST['dept'])) {
        $_REQUEST['dept'] = -1;
    }
    // which is never a valid department!
    if (!shd_allowed_to('shd_topic_to_ticket', $_REQUEST['dept']) || !empty($modSettings['shd_helpdesk_only']) || !empty($modSettings['shd_disable_tickettotopic'])) {
        fatal_lang_error('shd_cannot_move_topic', false);
    }
    if (empty($_REQUEST['topic'])) {
        fatal_lang_error('shd_no_topic');
    }
    $context['topic_id'] = (int) $_REQUEST['topic'];
    // Just in case, are they cancelling?
    if (isset($_REQUEST['cancel'])) {
        redirectexit('topic=' . $context['topic_id']);
    }
    if (isset($_POST['send_pm']) && (!isset($_POST['pm_content']) || trim($_POST['pm_content']) == '')) {
        fatal_lang_error('shd_move_no_pm_topic', false);
    }
    require_once $sourcedir . '/sd_source/Subs-SimpleDeskPost.php';
    // Fetch the topic information.
    $request = shd_db_query('', '
		SELECT m.subject, t.id_board, t.id_member_started, m.body, t.id_first_msg, m.smileys_enabled, t.id_member_updated, t.num_replies,
		m.poster_email, m.poster_name, m.poster_ip, m.poster_time, m.modified_time, m.modified_name, m.id_msg
		FROM {db_prefix}topics AS t
			INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
			INNER JOIN {db_prefix}boards AS b ON (t.id_board = b.id_board)
		WHERE {query_see_board} AND t.id_topic = {int:topic}
		LIMIT 1', array('topic' => $context['topic_id']));
    if ($smcFunc['db_num_rows']($request) == 0) {
        fatal_lang_error('shd_move_ticket_not_created');
    } else {
        list($subject, $board, $owner, $body, $firstmsg, $smileys_enabled, $memberupdated, $numreplies, $postername, $posteremail, $posterip, $postertime, $modified_time, $modified_name, $smf_id_msg) = $smcFunc['db_fetch_row']($request);
    }
    $smcFunc['db_free_result']($request);
    // Figure out what the status of the ticket should be.
    $status = shd_determine_status('topictoticket', $owner, $memberupdated, $numreplies, $_REQUEST['dept']);
    // Are we changing the subject?
    $old_subject = $subject;
    $subject = !empty($_POST['change_subject']) && !empty($_POST['subject']) ? $_POST['subject'] : $subject;
    // Just before we do this, make sure we call any hooks. $context and $_POST have lots of interesting things for us.
    call_integration_hook('shd_hook_topictoticket');
    // All okay, it seems. Let's go create the ticket.
    $msg_assoc = array();
    $msgOptions = array('body' => $body, 'smileys_enabled' => !empty($smileys_enabled) ? 1 : 0, 'modified' => array('time' => $modified_time, 'name' => $modified_name), 'time' => $postertime);
    $ticketOptions = array('dept' => $_REQUEST['dept'], 'subject' => $subject, 'mark_as_read' => false, 'private' => false, 'status' => $status, 'urgency' => 0, 'assigned' => 0);
    $posterOptions = array('id' => $owner, 'name' => $postername, 'email' => $posteremail, 'ip' => $posterip);
    shd_create_ticket_post($msgOptions, $ticketOptions, $posterOptions);
    $msg_assoc[$smf_id_msg] = $msgOptions['id'];
    // Ticket created, let's dig out the replies and post them in the ticket, if there are any.
    if (isset($ticketOptions['id'])) {
        $request = shd_db_query('', '
			SELECT body, id_member, poster_time, poster_name, poster_email, poster_ip, smileys_enabled, id_msg
			FROM {db_prefix}messages
			WHERE id_topic = {int:topic}
			AND id_msg != {int:topic_msg}', array('topic' => $context['topic_id'], 'topic_msg' => $firstmsg));
        $num_replies = $smcFunc['db_num_rows']($request) + 1;
        // Plus one since we want to count the main ticket post as well.
        // The ID of the ticket we created
        $ticket = $ticketOptions['id'];
        if ($smcFunc['db_num_rows']($request) != 0) {
            // Now loop through each reply and post it.  Hopefully there aren't too many. *looks at clock*
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                $msgOptions = array('body' => $row['body'], 'smileys_enabled' => !empty($row['smileys_enabled']) ? 1 : 0);
                $ticketOptions = array('id' => $ticket, 'mark_as_read' => false);
                $posterOptions = array('id' => $row['id_member'], 'name' => !empty($row['poster_name']) ? $row['poster_name'] : '', 'email' => !empty($row['poster_email']) ? $row['poster_email'] : '', 'ip' => !empty($row['poster_ip']) ? $row['poster_ip'] : '');
                shd_create_ticket_post($msgOptions, $ticketOptions, $posterOptions);
                $msg_assoc[$row['id_msg']] = $msgOptions['id'];
            }
        }
        // Ticket: check; Replies: check; Notfiy the topic starter, if desired.
        if (isset($_POST['send_pm'])) {
            require_once $sourcedir . '/Subs-Post.php';
            $request = shd_db_query('pm_find_username', '
				SELECT id_member, real_name
				FROM {db_prefix}members
				WHERE id_member = {int:user}
				LIMIT 1', array('user' => $owner));
            list($userid, $username) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
            // Fix the content
            $replacements = array('{user}' => $username, '{subject}' => $old_subject, '{link}' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $ticket);
            $message = str_replace(array_keys($replacements), array_values($replacements), $_POST['pm_content']);
            $recipients = array('to' => array($owner), 'bcc' => array());
            sendpm($recipients, $txt['shd_ticket_moved_subject_topic'], un_htmlspecialchars($message));
        }
        // And now for something completely different: attachments
        if (!empty($msg_assoc)) {
            // 1. Get all the attachments for these messages from the attachments table
            $attachIDs = array();
            $query = shd_db_query('', '
				SELECT id_attach, id_msg
				FROM {db_prefix}attachments
				WHERE id_msg IN ({array_int:smf_msgs})', array('smf_msgs' => array_keys($msg_assoc)));
            while ($row = $smcFunc['db_fetch_assoc']($query)) {
                $attachIDs[] = $row;
            }
            $smcFunc['db_free_result']($query);
            if (!empty($attachIDs)) {
                // 2. Do the switch
                // 2.1. Add them to SD's tables
                $array = array();
                foreach ($attachIDs as $attach) {
                    $array[] = array($attach['id_attach'], $ticket, $msg_assoc[$attach['id_msg']]);
                }
                $smcFunc['db_insert']('replace', '{db_prefix}helpdesk_attachments', array('id_attach' => 'int', 'id_ticket' => 'int', 'id_msg' => 'int'), $array, array('id_attach'));
                // 2.2. "Remove" them from SMF's table
                shd_db_query('', '
					UPDATE {db_prefix}attachments
					SET id_msg = 0
					WHERE id_msg IN ({array_int:smf_msgs})', array('smf_msgs' => array_keys($msg_assoc)));
            }
        }
        // Now we'll add this to the log.
        $log_params = array('subject' => $subject, 'ticket' => $ticket);
        shd_log_action('topictoticket', $log_params);
        // Update post counts.
        $request = shd_db_query('', '
			SELECT id_member
			FROM {db_prefix}messages
			WHERE id_topic = {int:topic}', array('topic' => $context['topic_id']));
        $posters = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            if (!isset($posters[$row['id_member']])) {
                $posters[$row['id_member']] = 0;
            }
            $posters[$row['id_member']]++;
        }
        $smcFunc['db_free_result']($request);
        foreach ($posters as $id_member => $posts) {
            updateMemberData($id_member, array('posts' => 'posts - ' . $posts));
        }
        // Lastly, delete the topic from the database.
        shd_db_query('', '
			DELETE FROM {db_prefix}topics
			WHERE id_topic = {int:topic}
			LIMIT 1', array('topic' => $context['topic_id']));
        // And the replies, too.
        shd_db_query('', '
			DELETE FROM {db_prefix}messages
			WHERE id_topic = {int:topic}', array('topic' => $context['topic_id']));
        // Update the stats.
        require_once $sourcedir . '/Subs-Post.php';
        updateStats('message');
        updateStats('topic');
        updateLastMessages($board);
        // Update board post counts.
        shd_db_query('', '
			UPDATE {db_prefix}boards
			SET num_topics = num_topics - 1,
				num_posts = num_posts - {int:num_posts}
			WHERE id_board = {int:board}', array('board' => $board, 'num_posts' => $num_replies));
    } else {
        fatal_lang_error('shd_move_ticket_not_created', false);
    }
    // Send them to the ticket.
    redirectexit('action=helpdesk;sa=ticket;ticket=' . $ticket);
}
Exemplo n.º 10
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) {
        // 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;
}
Exemplo n.º 11
0
function removeMessage($message, $decreasePostCount = true)
{
    global $db_prefix, $board, $sourcedir, $modSettings, $ID_MEMBER, $user_info;
    if (empty($message) || !is_numeric($message)) {
        return false;
    }
    $request = db_query("\n\t\tSELECT\n\t\t\tm.ID_MEMBER, m.icon, m.posterTime, m.subject," . (empty($modSettings['search_custom_index_config']) ? '' : ' m.body,') . "\n\t\t\tt.ID_TOPIC, t.ID_FIRST_MSG, t.ID_LAST_MSG, t.numReplies, t.ID_BOARD,\n\t\t\tt.ID_MEMBER_STARTED AS ID_MEMBER_POSTER,\n\t\t\tb.countPosts\n\t\tFROM ({$db_prefix}messages AS m, {$db_prefix}topics AS t, {$db_prefix}boards AS b)\n\t\tWHERE m.ID_MSG = {$message}\n\t\t\tAND t.ID_TOPIC = m.ID_TOPIC\n\t\t\tAND b.ID_BOARD = t.ID_BOARD\n\t\tLIMIT 1", __FILE__, __LINE__);
    if (mysql_num_rows($request) == 0) {
        return false;
    }
    $row = mysql_fetch_assoc($request);
    mysql_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'] == $ID_MEMBER) {
                if (!$delete_own) {
                    if ($row['ID_MEMBER_POSTER'] == $ID_MEMBER) {
                        if (!$delete_replies) {
                            fatal_lang_error('cannot_delete_replies');
                        }
                    } else {
                        fatal_lang_error('cannot_delete_own');
                    }
                } elseif (($row['ID_MEMBER_POSTER'] != $ID_MEMBER || !$delete_replies) && !empty($modSettings['edit_disable_time']) && $row['posterTime'] + $modSettings['edit_disable_time'] * 60 < time()) {
                    fatal_lang_error('modify_post_time_passed', false);
                }
            } elseif ($row['ID_MEMBER_POSTER'] == $ID_MEMBER) {
                if (!$delete_replies) {
                    fatal_lang_error('cannot_delete_replies');
                }
            } else {
                fatal_lang_error('cannot_delete_any');
            }
        }
    } else {
        // Check permissions to delete this message.
        if ($row['ID_MEMBER'] == $ID_MEMBER) {
            if (!allowedTo('delete_own')) {
                if ($row['ID_MEMBER_POSTER'] == $ID_MEMBER && !allowedTo('delete_any')) {
                    isAllowedTo('delete_replies');
                } elseif (!allowedTo('delete_any')) {
                    isAllowedTo('delete_own');
                }
            } elseif (!allowedTo('delete_any') && ($row['ID_MEMBER_POSTER'] != $ID_MEMBER || !allowedTo('delete_replies')) && !empty($modSettings['edit_disable_time']) && $row['posterTime'] + $modSettings['edit_disable_time'] * 60 < time()) {
                fatal_lang_error('modify_post_time_passed', false);
            }
        } elseif ($row['ID_MEMBER_POSTER'] == $ID_MEMBER && !allowedTo('delete_any')) {
            isAllowedTo('delete_replies');
        } else {
            isAllowedTo('delete_any');
        }
    }
    // 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'] != $ID_MEMBER && !$remove_any) {
                fatal_lang_error('cannot_remove_any');
            } elseif (!$remove_any && !$remove_own) {
                fatal_lang_error('cannot_remove_own');
            }
        } else {
            // Check permissions to delete a whole topic.
            if ($row['ID_MEMBER'] != $ID_MEMBER) {
                isAllowedTo('remove_any');
            } elseif (!allowedTo('remove_any')) {
                isAllowedTo('remove_own');
            }
        }
        // ...if there is only one post.
        if (!empty($row['numReplies'])) {
            fatal_lang_error('delFirstPost', false);
        }
        removeTopics($row['ID_TOPIC']);
        return true;
    }
    // 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("\n\t\t\tSELECT (IFNULL(lb.ID_MSG, 0) >= b.ID_MSG_UPDATED) AS isSeen\n\t\t\tFROM {$db_prefix}boards AS b\n\t\t\t\tLEFT JOIN {$db_prefix}log_boards AS lb ON (lb.ID_BOARD = b.ID_BOARD AND lb.ID_MEMBER = {$ID_MEMBER})\n\t\t\tWHERE b.ID_BOARD = {$modSettings['recycle_board']}", __FILE__, __LINE__);
        if (mysql_num_rows($request) == 0) {
            fatal_lang_error('recycle_no_valid_board');
        }
        list($isRead) = mysql_fetch_row($request);
        mysql_free_result($request);
        // Insert a new topic in the recycle board.
        db_query("\n\t\t\tINSERT INTO {$db_prefix}topics\n\t\t\t\t(ID_BOARD, ID_MEMBER_STARTED, ID_MEMBER_UPDATED, ID_FIRST_MSG, ID_LAST_MSG)\n\t\t\tVALUES ({$modSettings['recycle_board']}, {$row['ID_MEMBER']}, {$row['ID_MEMBER']}, {$message}, {$message})", __FILE__, __LINE__);
        // Capture the ID of the new topic...
        $topicID = db_insert_id();
        // If the topic creation went successful, move the message.
        if ($topicID > 0) {
            db_query("\n\t\t\t\tUPDATE {$db_prefix}messages\n\t\t\t\tSET \n\t\t\t\t\tID_TOPIC = {$topicID},\n\t\t\t\t\tID_BOARD = {$modSettings['recycle_board']},\n\t\t\t\t\ticon = 'recycled'\n\t\t\t\tWHERE ID_MSG = {$message}\n\t\t\t\tLIMIT 1", __FILE__, __LINE__);
            // Mark recycled topic as read.
            if (!$user_info['is_guest']) {
                db_query("\n\t\t\t\t\tREPLACE INTO {$db_prefix}log_topics\n\t\t\t\t\t\t(ID_TOPIC, ID_MEMBER, ID_MSG)\n\t\t\t\t\tVALUES ({$topicID}, {$ID_MEMBER}, {$modSettings['maxMsgID']})", __FILE__, __LINE__);
            }
            // Mark recycle board as seen, if it was marked as seen before.
            if (!empty($isRead) && !$user_info['is_guest']) {
                db_query("\n\t\t\t\t\tREPLACE INTO {$db_prefix}log_boards\n\t\t\t\t\t\t(ID_BOARD, ID_MEMBER, ID_MSG)\n\t\t\t\t\tVALUES ({$modSettings['recycle_board']}, {$ID_MEMBER}, {$modSettings['maxMsgID']})", __FILE__, __LINE__);
            }
            // Add one topic and post to the recycle bin board.
            db_query("\n\t\t\t\tUPDATE {$db_prefix}boards\n\t\t\t\tSET\n\t\t\t\t\tnumTopics = numTopics + 1,\n\t\t\t\t\tnumPosts = numPosts + 1\n\t\t\t\tWHERE ID_BOARD = {$modSettings['recycle_board']}\n\t\t\t\tLIMIT 1", __FILE__, __LINE__);
            // Make sure this message isn't getting deleted later on.
            $recycle = true;
            // Make sure we update the search subject index.
            updateStats('subject', $topicID, $row['subject']);
        }
    }
    // 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("\n\t\t\tSELECT ID_MSG, ID_MEMBER\n\t\t\tFROM {$db_prefix}messages\n\t\t\tWHERE ID_TOPIC = {$row['ID_TOPIC']}\n\t\t\t\tAND ID_MSG != {$message}\n\t\t\tORDER BY ID_MSG DESC\n\t\t\tLIMIT 1", __FILE__, __LINE__);
        $row2 = mysql_fetch_assoc($request);
        mysql_free_result($request);
        db_query("\n\t\t\tUPDATE {$db_prefix}topics\n\t\t\tSET\n\t\t\t\tID_LAST_MSG = {$row2['ID_MSG']},\n\t\t\t\tnumReplies = IF(numReplies = 0, 0, numReplies - 1),\n\t\t\t\tID_MEMBER_UPDATED = {$row2['ID_MEMBER']}\n\t\t\tWHERE ID_TOPIC = {$row['ID_TOPIC']}\n\t\t\tLIMIT 1", __FILE__, __LINE__);
    } else {
        db_query("\n\t\t\tUPDATE {$db_prefix}topics\n\t\t\tSET numReplies = IF(numReplies = 0, 0, numReplies - 1)\n\t\t\tWHERE ID_TOPIC = {$row['ID_TOPIC']}\n\t\t\tLIMIT 1", __FILE__, __LINE__);
    }
    db_query("\n\t\tUPDATE {$db_prefix}boards\n\t\tSET numPosts = IF(numPosts = 0, 0, numPosts - 1)\n\t\tWHERE ID_BOARD = {$row['ID_BOARD']}\n\t\tLIMIT 1", __FILE__, __LINE__);
    // 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['countPosts'])) {
        updateMemberData($row['ID_MEMBER'], array('posts' => '-'));
    }
    // Only remove posts if they're not recycled.
    if (!$recycle) {
        // Remove the message!
        db_query("\n\t\t\tDELETE FROM {$db_prefix}messages\n\t\t\tWHERE ID_MSG = {$message}\n\t\t\tLIMIT 1", __FILE__, __LINE__);
        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("\n\t\t\t\t\tDELETE FROM {$db_prefix}log_search_words\n\t\t\t\t\tWHERE ID_WORD IN (" . implode(', ', $words) . ")\n\t\t\t\t\t\tAND ID_MSG = {$message}", __FILE__, __LINE__);
            }
        }
        // Delete attachment(s) if they exist.
        require_once $sourcedir . '/ManageAttachments.php';
        removeAttachments('a.attachmentType = 0 AND a.ID_MSG = ' . $message);
    }
    // Update the pesky statistics.
    updateStats('message');
    updateStats('topic');
    updateStats('calendar');
    // And now to update the last message of each board we messed with.
    require_once $sourcedir . '/Subs-Post.php';
    if ($recycle) {
        updateLastMessages(array($row['ID_BOARD'], $modSettings['recycle_board']));
    } else {
        updateLastMessages($row['ID_BOARD']);
    }
    return false;
}
Exemplo n.º 12
0
function moveTopics($topics, $toBoard)
{
    global $db_prefix, $sourcedir, $ID_MEMBER, $user_info, $modSettings;
    // Empty array?
    if (empty($topics)) {
        return;
    } elseif (is_numeric($topics)) {
        $condition = '= ' . $topics;
    } elseif (count($topics) == 1) {
        $condition = '= ' . $topics[0];
    } else {
        $condition = 'IN (' . implode(', ', $topics) . ')';
    }
    $numTopics = count($topics);
    $fromBoards = array();
    // Destination board empty or equal to 0?
    if (empty($toBoard)) {
        return;
    }
    // Determine the source boards...
    $request = db_query("\n\t\tSELECT ID_BOARD, COUNT(*) AS numTopics, SUM(numReplies) AS numReplies\n\t\tFROM {$db_prefix}topics\n\t\tWHERE ID_TOPIC {$condition}\n\t\tGROUP BY ID_BOARD", __FILE__, __LINE__);
    // Num of rows = 0 -> no topics found. Num of rows > 1 -> topics are on multiple boards.
    if (mysql_num_rows($request) == 0) {
        return;
    }
    while ($row = mysql_fetch_assoc($request)) {
        // Posts = (numReplies + 1) for each topic.
        $fromBoards[$row['ID_BOARD']] = array('numPosts' => $row['numReplies'] + $row['numTopics'], 'numTopics' => $row['numTopics'], 'ID_BOARD' => $row['ID_BOARD']);
    }
    mysql_free_result($request);
    // Move over the mark_read data. (because it may be read and now not by some!)
    $SaveAServer = max(0, $modSettings['maxMsgID'] - 50000);
    $request = db_query("\n\t\tSELECT lmr.ID_MEMBER, lmr.ID_MSG, t.ID_TOPIC\n\t\tFROM ({$db_prefix}topics AS t, {$db_prefix}log_mark_read AS lmr)\n\t\t\tLEFT JOIN {$db_prefix}log_topics AS lt ON (lt.ID_TOPIC = t.ID_TOPIC AND lt.ID_MEMBER = lmr.ID_MEMBER)\n\t\tWHERE t.ID_TOPIC {$condition}\n\t\t\tAND lmr.ID_BOARD = t.ID_BOARD\n\t\t\tAND lmr.ID_MSG > t.ID_FIRST_MSG\n\t\t\tAND lmr.ID_MSG > {$SaveAServer}\n\t\t\tAND lmr.ID_MSG > IFNULL(lt.ID_MSG, 0)", __FILE__, __LINE__);
    $log_topics = array();
    while ($row = mysql_fetch_assoc($request)) {
        $log_topics[] = '(' . $row['ID_TOPIC'] . ', ' . $row['ID_MEMBER'] . ', ' . $row['ID_MSG'] . ')';
        // Prevent queries from getting too big. Taking some steam off.
        if (count($log_topics) > 500) {
            db_query("\n\t\t\t\tREPLACE INTO {$db_prefix}log_topics\n\t\t\t\t\t(ID_TOPIC, ID_MEMBER, ID_MSG)\n\t\t\t\tVALUES " . implode(',
					', $log_topics), __FILE__, __LINE__);
            $log_topics = array();
        }
    }
    mysql_free_result($request);
    // Now that we have all the topics that *should* be marked read, and by which members...
    if (!empty($log_topics)) {
        // Insert that information into the database!
        db_query("\n\t\t\tREPLACE INTO {$db_prefix}log_topics\n\t\t\t\t(ID_TOPIC, ID_MEMBER, ID_MSG)\n\t\t\tVALUES " . implode(',
				', $log_topics), __FILE__, __LINE__);
    }
    // Update the number of posts on each board.
    $totalTopics = 0;
    $totalPosts = 0;
    foreach ($fromBoards as $stats) {
        db_query("\n\t\t\tUPDATE {$db_prefix}boards\n\t\t\tSET \n\t\t\t\tnumPosts = IF({$stats['numPosts']} > numPosts, 0, numPosts - {$stats['numPosts']}),\n\t\t\t\tnumTopics = IF({$stats['numTopics']} > numTopics, 0, numTopics - {$stats['numTopics']})\n\t\t\tWHERE ID_BOARD = {$stats['ID_BOARD']}\n\t\t\tLIMIT 1", __FILE__, __LINE__);
        $totalTopics += $stats['numTopics'];
        $totalPosts += $stats['numPosts'];
    }
    db_query("\n\t\tUPDATE {$db_prefix}boards\n\t\tSET \n\t\t\tnumTopics = numTopics + {$totalTopics}, \n\t\t\tnumPosts = numPosts + {$totalPosts}\n\t\tWHERE ID_BOARD = {$toBoard}\n\t\tLIMIT 1", __FILE__, __LINE__);
    // Move the topic.  Done.  :P
    db_query("\n\t\tUPDATE {$db_prefix}topics\n\t\tSET ID_BOARD = {$toBoard}\n\t\tWHERE ID_TOPIC {$condition}\n\t\tLIMIT {$numTopics}", __FILE__, __LINE__);
    db_query("\n\t\tUPDATE {$db_prefix}messages\n\t\tSET ID_BOARD = {$toBoard}\n\t\tWHERE ID_TOPIC {$condition}", __FILE__, __LINE__);
    db_query("\n\t\tUPDATE {$db_prefix}calendar\n\t\tSET ID_BOARD = {$toBoard}\n\t\tWHERE ID_TOPIC {$condition}\n\t\tLIMIT {$numTopics}", __FILE__, __LINE__);
    // Mark target board as seen, if it was already marked as seen before.
    $request = db_query("\n\t\tSELECT (IFNULL(lb.ID_MSG, 0) >= b.ID_MSG_UPDATED) AS isSeen\n\t\tFROM {$db_prefix}boards AS b\n\t\t\tLEFT JOIN {$db_prefix}log_boards AS lb ON (lb.ID_BOARD = b.ID_BOARD AND lb.ID_MEMBER = {$ID_MEMBER})\n\t\tWHERE b.ID_BOARD = {$toBoard}", __FILE__, __LINE__);
    list($isSeen) = mysql_fetch_row($request);
    mysql_free_result($request);
    if (!empty($isSeen) && !$user_info['is_guest']) {
        db_query("\n\t\t\tREPLACE INTO {$db_prefix}log_boards\n\t\t\t\t(ID_BOARD, ID_MEMBER, ID_MSG)\n\t\t\tVALUES ({$toBoard}, {$ID_MEMBER}, {$modSettings['maxMsgID']})", __FILE__, __LINE__);
    }
    // Update 'em pesky stats.
    updateStats('topic');
    updateStats('message');
    updateStats('calendar');
    require_once $sourcedir . '/Subs-Post.php';
    $updates = array_keys($fromBoards);
    $updates[] = $toBoard;
    updateLastMessages(array_unique($updates));
}
Exemplo n.º 13
0
function QuickModeration()
{
    global $db_prefix, $sourcedir, $board, $ID_MEMBER, $modSettings, $sourcedir;
    // Check the session = get or post.
    checkSession('request');
    if (isset($_SESSION['topicseen_cache'])) {
        $_SESSION['topicseen_cache'] = array();
    }
    // This is going to be needed to send off the notifications and for updateLastMessages().
    require_once $sourcedir . '/Subs-Post.php';
    // Remember the last board they moved things to.
    if (isset($_REQUEST['move_to'])) {
        $_SESSION['move_to_topic'] = $_REQUEST['move_to'];
    }
    // Only a few possible actions.
    $possibleActions = array('markread');
    if (!empty($board)) {
        $boards_can = array('make_sticky' => allowedTo('make_sticky') ? array($board) : array(), 'move_any' => allowedTo('move_any') ? array($board) : array(), 'move_own' => allowedTo('move_own') ? array($board) : array(), 'remove_any' => allowedTo('remove_any') ? array($board) : array(), 'remove_own' => allowedTo('remove_own') ? array($board) : array(), 'lock_any' => allowedTo('lock_any') ? array($board) : array(), 'lock_own' => allowedTo('lock_own') ? array($board) : array(), 'merge_any' => allowedTo('merge_any') ? array($board) : array());
        $redirect_url = 'board=' . $board . '.' . $_REQUEST['start'];
    } else {
        // !!! Ugly.  There's no getting around this, is there?
        $boards_can = array('make_sticky' => boardsAllowedTo('make_sticky'), 'move_any' => boardsAllowedTo('move_any'), 'move_own' => boardsAllowedTo('move_own'), 'remove_any' => boardsAllowedTo('remove_any'), 'remove_own' => boardsAllowedTo('remove_own'), 'lock_any' => boardsAllowedTo('lock_any'), 'lock_own' => boardsAllowedTo('lock_own'), 'merge_any' => boardsAllowedTo('merge_any'));
        $redirect_url = isset($_POST['redirect_url']) ? $_POST['redirect_url'] : (isset($_SESSION['old_url']) ? $_SESSION['old_url'] : '');
    }
    if (!empty($boards_can['make_sticky']) && !empty($modSettings['enableStickyTopics'])) {
        $possibleActions[] = 'sticky';
    }
    if (!empty($boards_can['move_any']) || !empty($boards_can['move_own'])) {
        $possibleActions[] = 'move';
    }
    if (!empty($boards_can['remove_any']) || !empty($boards_can['remove_own'])) {
        $possibleActions[] = 'remove';
    }
    if (!empty($boards_can['lock_any']) || !empty($boards_can['lock_own'])) {
        $possibleActions[] = 'lock';
    }
    if (!empty($boards_can['merge_any'])) {
        $possibleActions[] = 'merge';
    }
    // Two methods: $_REQUEST['actions'] (ID_TOPIC => action), and $_REQUEST['topics'] and $_REQUEST['qaction'].
    // (if action is 'move', $_REQUEST['move_to'] or $_REQUEST['move_tos'][$topic] is used.)
    if (!empty($_REQUEST['topics'])) {
        // If the action isn't valid, just quit now.
        if (empty($_REQUEST['qaction']) || !in_array($_REQUEST['qaction'], $possibleActions)) {
            redirectexit($redirect_url);
        }
        // Merge requires all topics as one parameter and can be done at once.
        if ($_REQUEST['qaction'] == 'merge') {
            // Merge requires at least two topics.
            if (empty($_REQUEST['topics']) || count($_REQUEST['topics']) < 2) {
                redirectexit($redirect_url);
            }
            require_once $sourcedir . '/SplitTopics.php';
            return MergeExecute($_REQUEST['topics']);
        }
        // Just convert to the other method, to make it easier.
        foreach ($_REQUEST['topics'] as $topic) {
            $_REQUEST['actions'][(int) $topic] = $_REQUEST['qaction'];
        }
    }
    // Weird... how'd you get here?
    if (empty($_REQUEST['actions'])) {
        redirectexit($redirect_url);
    }
    // Validate each action.
    $temp = array();
    foreach ($_REQUEST['actions'] as $topic => $action) {
        if (in_array($action, $possibleActions)) {
            $temp[(int) $topic] = $action;
        }
    }
    $_REQUEST['actions'] = $temp;
    if (!empty($_REQUEST['actions'])) {
        // Find all topics that *aren't* on this board.
        $request = db_query("\n\t\t\tSELECT ID_TOPIC, ID_MEMBER_STARTED, ID_BOARD, locked\n\t\t\tFROM {$db_prefix}topics\n\t\t\tWHERE ID_TOPIC IN (" . implode(', ', array_keys($_REQUEST['actions'])) . ")" . (!empty($board) ? "\n\t\t\t\tAND ID_BOARD != {$board}" : '') . "\n\t\t\tLIMIT " . count($_REQUEST['actions']), __FILE__, __LINE__);
        while ($row = mysql_fetch_assoc($request)) {
            if (!empty($board)) {
                unset($_REQUEST['actions'][$row['ID_TOPIC']]);
            } else {
                // Goodness, this is fun.  We need to validate the action.
                if ($_REQUEST['actions'][$row['ID_TOPIC']] == 'sticky' && !in_array(0, $boards_can['make_sticky']) && !in_array($row['ID_BOARD'], $boards_can['make_sticky'])) {
                    unset($_REQUEST['actions'][$row['ID_TOPIC']]);
                } elseif ($_REQUEST['actions'][$row['ID_TOPIC']] == 'move' && !in_array(0, $boards_can['move_any']) && !in_array($row['ID_BOARD'], $boards_can['move_any']) && ($row['ID_MEMBER_STARTED'] != $ID_MEMBER || !in_array(0, $boards_can['move_own']) && !in_array($row['ID_BOARD'], $boards_can['move_own']))) {
                    unset($_REQUEST['actions'][$row['ID_TOPIC']]);
                } elseif ($_REQUEST['actions'][$row['ID_TOPIC']] == 'remove' && !in_array(0, $boards_can['remove_any']) && !in_array($row['ID_BOARD'], $boards_can['remove_any']) && ($row['ID_MEMBER_STARTED'] != $ID_MEMBER || !in_array(0, $boards_can['remove_own']) && !in_array($row['ID_BOARD'], $boards_can['remove_own']))) {
                    unset($_REQUEST['actions'][$row['ID_TOPIC']]);
                } elseif ($_REQUEST['actions'][$row['ID_TOPIC']] == 'lock' && !in_array(0, $boards_can['lock_any']) && !in_array($row['ID_BOARD'], $boards_can['lock_any']) && ($row['ID_MEMBER_STARTED'] != $ID_MEMBER || $locked == 1 || !in_array(0, $boards_can['lock_own']) && !in_array($row['ID_BOARD'], $boards_can['lock_own']))) {
                    unset($_REQUEST['actions'][$row['ID_TOPIC']]);
                }
            }
        }
        mysql_free_result($request);
    }
    $stickyCache = array();
    $moveCache = array(0 => array(), 1 => array());
    $removeCache = array();
    $lockCache = array();
    $markCache = array();
    // Separate the actions.
    foreach ($_REQUEST['actions'] as $topic => $action) {
        $topic = (int) $topic;
        if ($action == 'markread') {
            $markCache[] = $topic;
        } elseif ($action == 'sticky') {
            $stickyCache[] = $topic;
        } elseif ($action == 'move') {
            // $moveCache[0] is the topic, $moveCache[1] is the board to move to.
            $moveCache[1][$topic] = (int) (isset($_REQUEST['move_tos'][$topic]) ? $_REQUEST['move_tos'][$topic] : $_REQUEST['move_to']);
            if (empty($moveCache[1][$topic])) {
                continue;
            }
            $moveCache[0][] = $topic;
        } elseif ($action == 'remove') {
            $removeCache[] = $topic;
        } elseif ($action == 'lock') {
            $lockCache[] = $topic;
        }
    }
    if (empty($board)) {
        $affectedBoards = array();
    } else {
        $affectedBoards = array($board => array(0, 0));
    }
    // Do all the stickies...
    if (!empty($stickyCache)) {
        db_query("\n\t\t\tUPDATE {$db_prefix}topics\n\t\t\tSET isSticky = IF(isSticky = 1, 0, 1)\n\t\t\tWHERE ID_TOPIC IN (" . implode(', ', $stickyCache) . ")\n\t\t\tLIMIT " . count($stickyCache), __FILE__, __LINE__);
    }
    // Move sucka! (this is, by the by, probably the most complicated part....)
    if (!empty($moveCache[0])) {
        // I know - I just KNOW you're trying to beat the system.  Too bad for you... we CHECK :P.
        $request = db_query("\n\t\t\tSELECT numReplies, ID_TOPIC, ID_BOARD\n\t\t\tFROM {$db_prefix}topics\n\t\t\tWHERE ID_TOPIC IN (" . implode(', ', $moveCache[0]) . ")" . (!empty($board) && !allowedTo('move_any') ? "\n\t\t\t\tAND ID_MEMBER_STARTED = {$ID_MEMBER}" : '') . "\n\t\t\tLIMIT " . count($moveCache[0]), __FILE__, __LINE__);
        $moveCache2 = array();
        while ($row = mysql_fetch_assoc($request)) {
            $to = $moveCache[1][$row['ID_TOPIC']];
            $row['numReplies']++;
            if (empty($to)) {
                continue;
            }
            if (!isset($affectedBoards[$to])) {
                $affectedBoards[$to] = array(0, 0);
            }
            if (!isset($affectedBoards[$row['ID_BOARD']])) {
                $affectedBoards[$row['ID_BOARD']] = array(0, 0);
            }
            $affectedBoards[$row['ID_BOARD']][0]--;
            $affectedBoards[$row['ID_BOARD']][1] -= $row['numReplies'];
            $affectedBoards[$to][0]++;
            $affectedBoards[$to][1] += $row['numReplies'];
            // Move the actual topic.
            db_query("\n\t\t\t\tUPDATE {$db_prefix}topics\n\t\t\t\tSET ID_BOARD = {$to}\n\t\t\t\tWHERE ID_TOPIC = {$row['ID_TOPIC']}\n\t\t\t\tLIMIT 1", __FILE__, __LINE__);
            db_query("\n\t\t\t\tUPDATE {$db_prefix}messages\n\t\t\t\tSET ID_BOARD = {$to}\n\t\t\t\tWHERE ID_TOPIC = {$row['ID_TOPIC']}", __FILE__, __LINE__);
            db_query("\n\t\t\t\tUPDATE {$db_prefix}calendar\n\t\t\t\tSET ID_BOARD = {$to}\n\t\t\t\tWHERE ID_TOPIC = {$row['ID_TOPIC']}", __FILE__, __LINE__);
            $moveCache2[] = array($row['ID_TOPIC'], $row['ID_BOARD'], $to);
        }
        mysql_free_result($request);
        $moveCache = $moveCache2;
        foreach ($affectedBoards as $ID_BOARD => $topicsPosts) {
            db_query("\n\t\t\t\tUPDATE {$db_prefix}boards\n\t\t\t\tSET numPosts = numPosts + {$topicsPosts['1']}, numTopics = numTopics + {$topicsPosts['0']}\n\t\t\t\tWHERE ID_BOARD = {$ID_BOARD}\n\t\t\t\tLIMIT 1", __FILE__, __LINE__);
        }
    }
    // Now delete the topics...
    if (!empty($removeCache)) {
        // They can only delete their own topics. (we wouldn't be here if they couldn't do that..)
        if (!empty($board) && !allowedTo('remove_any')) {
            $result = db_query("\n\t\t\t\tSELECT ID_TOPIC\n\t\t\t\tFROM {$db_prefix}topics\n\t\t\t\tWHERE ID_TOPIC IN (" . implode(', ', $removeCache) . ")\n\t\t\t\t\tAND ID_MEMBER_STARTED = {$ID_MEMBER}\n\t\t\t\tLIMIT " . count($removeCache), __FILE__, __LINE__);
            $removeCache = array();
            while ($row = mysql_fetch_assoc($result)) {
                $removeCache[] = $row['ID_TOPIC'];
            }
            mysql_free_result($result);
        }
        // Maybe *none* were their own topics.
        if (!empty($removeCache)) {
            // Gotta send the notifications *first*!
            foreach ($removeCache as $topic) {
                logAction('remove', array('topic' => $topic));
                sendNotifications($topic, 'remove');
            }
            require_once $sourcedir . '/RemoveTopic.php';
            removeTopics($removeCache);
        }
    }
    // And lastly, lock the topics...
    if (!empty($lockCache)) {
        $lockStatus = array();
        // Gotta make sure they CAN lock/unlock these topics...
        if (!empty($board) && !allowedTo('lock_any')) {
            // Make sure they started the topic AND it isn't already locked by someone with higher priv's.
            $result = db_query("\n\t\t\t\tSELECT ID_TOPIC, locked\n\t\t\t\tFROM {$db_prefix}topics\n\t\t\t\tWHERE ID_TOPIC IN (" . implode(', ', $lockCache) . ")\n\t\t\t\t\tAND ID_MEMBER_STARTED = {$ID_MEMBER}\n\t\t\t\t\tAND locked IN (2, 0)\n\t\t\t\tLIMIT " . count($lockCache), __FILE__, __LINE__);
            $lockCache = array();
            while ($row = mysql_fetch_assoc($result)) {
                $lockCache[] = $row['ID_TOPIC'];
                $lockStatus[$row['ID_TOPIC']] = empty($row['locked']);
            }
            mysql_free_result($result);
        } else {
            $result = db_query("\n\t\t\t\tSELECT ID_TOPIC, locked\n\t\t\t\tFROM {$db_prefix}topics\n\t\t\t\tWHERE ID_TOPIC IN (" . implode(', ', $lockCache) . ")\n\t\t\t\tLIMIT " . count($lockCache), __FILE__, __LINE__);
            while ($row = mysql_fetch_assoc($result)) {
                $lockStatus[$row['ID_TOPIC']] = empty($row['locked']);
            }
            mysql_free_result($result);
        }
        // It could just be that *none* were their own topics...
        if (!empty($lockCache)) {
            // Alternate the locked value.
            db_query("\n\t\t\t\tUPDATE {$db_prefix}topics\n\t\t\t\tSET locked = IF(locked = 0, " . (allowedTo('lock_any') ? '1' : '2') . ", 0)\n\t\t\t\tWHERE ID_TOPIC IN (" . implode(', ', $lockCache) . ")\n\t\t\t\tLIMIT " . count($lockCache), __FILE__, __LINE__);
        }
    }
    if (!empty($markCache)) {
        $setString = '';
        foreach ($markCache as $topic) {
            $setString .= "\n\t\t\t\t({$modSettings['maxMsgID']}, {$ID_MEMBER}, {$topic}),";
        }
        db_query("\n\t\t\tREPLACE INTO {$db_prefix}log_topics\n\t\t\t\t(ID_MSG, ID_MEMBER, ID_TOPIC)\n\t\t\tVALUES" . substr($setString, 0, -1), __FILE__, __LINE__);
    }
    foreach ($moveCache as $topic) {
        // Didn't actually move anything!
        if (!isset($topic[0])) {
            break;
        }
        logAction('move', array('topic' => $topic[0], 'board_from' => $topic[1], 'board_to' => $topic[2]));
        sendNotifications($topic[0], 'move');
    }
    foreach ($lockCache as $topic) {
        logAction('lock', array('topic' => $topic));
        sendNotifications($topic, $lockStatus ? 'lock' : 'unlock');
    }
    foreach ($stickyCache as $topic) {
        logAction('sticky', array('topic' => $topic));
        sendNotifications($topic, 'sticky');
    }
    updateStats('topic');
    updateStats('message');
    updateStats('calendar');
    if (!empty($affectedBoards)) {
        updateLastMessages(array_keys($affectedBoards));
    }
    redirectexit($redirect_url);
}
Exemplo n.º 14
0
function createPost(&$msgOptions, &$topicOptions, &$posterOptions)
{
    global $db_prefix, $user_info, $ID_MEMBER, $txt, $modSettings;
    // Set optional parameters to the default value.
    $msgOptions['icon'] = empty($msgOptions['icon']) ? 'xx' : $msgOptions['icon'];
    $msgOptions['smileys_enabled'] = !empty($msgOptions['smileys_enabled']);
    $msgOptions['attachments'] = empty($msgOptions['attachments']) ? array() : $msgOptions['attachments'];
    $topicOptions['id'] = empty($topicOptions['id']) ? 0 : (int) $topicOptions['id'];
    $topicOptions['poll'] = isset($topicOptions['poll']) ? (int) $topicOptions['poll'] : null;
    $topicOptions['lock_mode'] = isset($topicOptions['lock_mode']) ? $topicOptions['lock_mode'] : null;
    $topicOptions['sticky_mode'] = isset($topicOptions['sticky_mode']) ? $topicOptions['sticky_mode'] : null;
    $posterOptions['id'] = empty($posterOptions['id']) ? 0 : (int) $posterOptions['id'];
    $posterOptions['ip'] = empty($posterOptions['ip']) ? $user_info['ip2'] : $posterOptions['ip'];
    // If nothing was filled in as name/e-mail address, try the member table.
    if (!isset($posterOptions['name']) || $posterOptions['name'] == '' || empty($posterOptions['email']) && !empty($posterOptions['id'])) {
        if (empty($posterOptions['id'])) {
            $posterOptions['id'] = 0;
            $posterOptions['name'] = $txt[28];
            $posterOptions['email'] = '';
        } elseif ($posterOptions['id'] != $ID_MEMBER) {
            $request = db_query("\n\t\t\t\tSELECT memberName, emailAddress\n\t\t\t\tFROM {$db_prefix}members\n\t\t\t\tWHERE ID_MEMBER = {$posterOptions['id']}\n\t\t\t\tLIMIT 1", __FILE__, __LINE__);
            // Couldn't find the current poster?
            if (mysql_num_rows($request) == 0) {
                trigger_error('createPost(): Invalid member id ' . $posterOptions['id'], E_USER_NOTICE);
                $posterOptions['id'] = 0;
                $posterOptions['name'] = $txt[28];
                $posterOptions['email'] = '';
            } else {
                list($posterOptions['name'], $posterOptions['email']) = mysql_fetch_row($request);
            }
            mysql_free_result($request);
        } else {
            $posterOptions['name'] = $user_info['name'];
            $posterOptions['email'] = $user_info['email'];
        }
        $posterOptions['email'] = addslashes($posterOptions['email']);
    }
    // It's do or die time: forget any user aborts!
    $previous_ignore_user_abort = ignore_user_abort(true);
    $new_topic = empty($topicOptions['id']);
    // Insert the post.
    db_query("\n\t\tINSERT INTO {$db_prefix}messages\n\t\t\t(ID_BOARD, ID_TOPIC, ID_MEMBER, subject, body, posterName, posterEmail, posterTime,\n\t\t\tposterIP, smileysEnabled, modifiedName, icon)\n\t\tVALUES ({$topicOptions['board']}, {$topicOptions['id']}, {$posterOptions['id']}, SUBSTRING('{$msgOptions['subject']}', 1, 255), SUBSTRING('{$msgOptions['body']}', 1, 65534), SUBSTRING('{$posterOptions['name']}', 1, 255), SUBSTRING('{$posterOptions['email']}', 1, 255), " . time() . ",\n\t\t\tSUBSTRING('{$posterOptions['ip']}', 1, 255), " . ($msgOptions['smileys_enabled'] ? '1' : '0') . ", '', SUBSTRING('{$msgOptions['icon']}', 1, 16))", __FILE__, __LINE__);
    $msgOptions['id'] = db_insert_id();
    // Something went wrong creating the message...
    if (empty($msgOptions['id'])) {
        return false;
    }
    // Fix the attachments.
    if (!empty($msgOptions['attachments'])) {
        db_query("\n\t\t\tUPDATE {$db_prefix}attachments\n\t\t\tSET ID_MSG = {$msgOptions['id']}\n\t\t\tWHERE ID_ATTACH IN (" . implode(', ', $msgOptions['attachments']) . ')', __FILE__, __LINE__);
    }
    // Insert a new topic (if the topicID was left empty.
    if ($new_topic) {
        db_query("\n\t\t\tINSERT INTO {$db_prefix}topics\n\t\t\t\t(ID_BOARD, ID_MEMBER_STARTED, ID_MEMBER_UPDATED, ID_FIRST_MSG, ID_LAST_MSG, locked, isSticky, numViews, ID_POLL)\n\t\t\tVALUES ({$topicOptions['board']}, {$posterOptions['id']}, {$posterOptions['id']}, {$msgOptions['id']}, {$msgOptions['id']},\n\t\t\t\t" . ($topicOptions['lock_mode'] === null ? '0' : $topicOptions['lock_mode']) . ', ' . ($topicOptions['sticky_mode'] === null ? '0' : $topicOptions['sticky_mode']) . ", 0, " . ($topicOptions['poll'] === null ? '0' : $topicOptions['poll']) . ')', __FILE__, __LINE__);
        $topicOptions['id'] = db_insert_id();
        // The topic couldn't be created for some reason.
        if (empty($topicOptions['id'])) {
            // We should delete the post that did work, though...
            db_query("\n\t\t\t\tDELETE FROM {$db_prefix}messages\n\t\t\t\tWHERE ID_MSG = {$msgOptions['id']}\n\t\t\t\tLIMIT 1", __FILE__, __LINE__);
            return false;
        }
        // Fix the message with the topic.
        db_query("\n\t\t\tUPDATE {$db_prefix}messages\n\t\t\tSET ID_TOPIC = {$topicOptions['id']}\n\t\t\tWHERE ID_MSG = {$msgOptions['id']}\n\t\t\tLIMIT 1", __FILE__, __LINE__);
        // There's been a new topic AND a new post today.
        trackStats(array('topics' => '+', 'posts' => '+'));
        updateStats('topic', true);
        updateStats('subject', $topicOptions['id'], $msgOptions['subject']);
    } else {
        // Update the number of replies and the lock/sticky status.
        db_query("\n\t\t\tUPDATE {$db_prefix}topics\n\t\t\tSET\n\t\t\t\tID_MEMBER_UPDATED = {$posterOptions['id']}, ID_LAST_MSG = {$msgOptions['id']},\n\t\t\t\tnumReplies = numReplies + 1" . ($topicOptions['lock_mode'] === null ? '' : ",\n\t\t\t\tlocked = {$topicOptions['lock_mode']}") . ($topicOptions['sticky_mode'] === null ? '' : ",\n\t\t\t\tisSticky = {$topicOptions['sticky_mode']}") . "\n\t\t\tWHERE ID_TOPIC = {$topicOptions['id']}\n\t\t\tLIMIT 1", __FILE__, __LINE__);
        // One new post has been added today.
        trackStats(array('posts' => '+'));
    }
    // Creating is modifying...in a way.
    db_query("\n\t\tUPDATE {$db_prefix}messages\n\t\tSET ID_MSG_MODIFIED = {$msgOptions['id']}\n\t\tWHERE ID_MSG = {$msgOptions['id']}", __FILE__, __LINE__);
    // Increase the number of posts and topics on the board.
    db_query("\n\t\tUPDATE {$db_prefix}boards\n\t\tSET numPosts = numPosts + 1" . ($new_topic ? ', numTopics = numTopics + 1' : '') . "\n\t\tWHERE ID_BOARD = {$topicOptions['board']}\n\t\tLIMIT 1", __FILE__, __LINE__);
    // Mark inserted topic as read (only for the user calling this function).
    if (!empty($topicOptions['mark_as_read']) && !$user_info['is_guest']) {
        // Since it's likely they *read* it before replying, let's try an UPDATE first.
        if (!$new_topic) {
            db_query("\n\t\t\t\tUPDATE {$db_prefix}log_topics\n\t\t\t\tSET ID_MSG = {$msgOptions['id']} + 1\n\t\t\t\tWHERE ID_MEMBER = {$ID_MEMBER}\n\t\t\t\t\tAND ID_TOPIC = {$topicOptions['id']}\n\t\t\t\tLIMIT 1", __FILE__, __LINE__);
            $flag = db_affected_rows() != 0;
        }
        if (empty($flag)) {
            db_query("\n\t\t\t\tREPLACE INTO {$db_prefix}log_topics\n\t\t\t\t\t(ID_TOPIC, ID_MEMBER, ID_MSG)\n\t\t\t\tVALUES ({$topicOptions['id']}, {$ID_MEMBER}, {$msgOptions['id']} + 1)", __FILE__, __LINE__);
        }
    }
    // If there's a custom search index, it needs updating...
    if (!empty($modSettings['search_custom_index_config'])) {
        //$index_settings = unserialize($modSettings['search_custom_index_config']);
        $inserts = '';
        foreach (text2words(stripslashes($msgOptions['body']), 4, true) as $word) {
            $inserts .= "({$word}, {$msgOptions['id']}),\n";
        }
        if (!empty($inserts)) {
            db_query("\n\t\t\t\tINSERT IGNORE INTO {$db_prefix}log_search_words\n\t\t\t\t\t(ID_WORD, ID_MSG)\n\t\t\t\tVALUES\n\t\t\t\t\t" . substr($inserts, 0, -2), __FILE__, __LINE__);
        }
    }
    // Increase the post counter for the user that created the post.
    if (!empty($posterOptions['update_post_count']) && !empty($posterOptions['id'])) {
        // Are you the one that happened to create this post?
        if ($ID_MEMBER == $posterOptions['id']) {
            $user_info['posts']++;
        }
        updateMemberData($posterOptions['id'], array('posts' => '+'));
    }
    // They've posted, so they can make the view count go up one if they really want. (this is to keep views >= replies...)
    $_SESSION['last_read_topic'] = 0;
    // Better safe than sorry.
    if (isset($_SESSION['topicseen_cache'][$topicOptions['board']])) {
        $_SESSION['topicseen_cache'][$topicOptions['board']]--;
    }
    // Update all the stats so everyone knows about this new topic and message.
    updateStats('message', true, $msgOptions['id']);
    updateLastMessages($topicOptions['board'], $msgOptions['id']);
    // Alright, done now... we can abort now, I guess... at least this much is done.
    ignore_user_abort($previous_ignore_user_abort);
    // Success.
    return true;
}
Exemplo n.º 15
0
/**
 * General function to split off a topic.
 * creates a new topic and moves the messages with the IDs in
 * array messagesToBeSplit to the new topic.
 * the subject of the newly created topic is set to 'newSubject'.
 * marks the newly created message as read for the user splitting it.
 * updates the statistics to reflect a newly created topic.
 * logs the action in the moderation log.
 * a notification is sent to all users monitoring this topic.
 *
 * @param int $split1_ID_TOPIC
 * @param int[] $splitMessages
 * @param string $new_subject
 * @return int the topic ID of the new split topic.
 */
function splitTopic($split1_ID_TOPIC, $splitMessages, $new_subject)
{
    global $txt;
    $db = database();
    // Nothing to split?
    if (empty($splitMessages)) {
        fatal_lang_error('no_posts_selected', false);
    }
    // Get some board info.
    $request = $db->query('', '
		SELECT id_board, approved
		FROM {db_prefix}topics
		WHERE id_topic = {int:id_topic}
		LIMIT 1', array('id_topic' => $split1_ID_TOPIC));
    list($id_board, $split1_approved) = $db->fetch_row($request);
    $db->free_result($request);
    // Find the new first and last not in the list. (old topic)
    $request = $db->query('', '
		SELECT
			MIN(m.id_msg) AS myid_first_msg, MAX(m.id_msg) AS myid_last_msg, COUNT(*) AS message_count, m.approved
		FROM {db_prefix}messages AS m
			INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:id_topic})
		WHERE m.id_msg NOT IN ({array_int:no_msg_list})
			AND m.id_topic = {int:id_topic}
		GROUP BY m.approved
		ORDER BY m.approved DESC
		LIMIT 2', array('id_topic' => $split1_ID_TOPIC, 'no_msg_list' => $splitMessages));
    // You can't select ALL the messages!
    if ($db->num_rows($request) == 0) {
        fatal_lang_error('selected_all_posts', false);
    }
    $split1_first_msg = null;
    $split1_last_msg = null;
    while ($row = $db->fetch_assoc($request)) {
        // Get the right first and last message dependant on approved state...
        if (empty($split1_first_msg) || $row['myid_first_msg'] < $split1_first_msg) {
            $split1_first_msg = $row['myid_first_msg'];
        }
        if (empty($split1_last_msg) || $row['approved']) {
            $split1_last_msg = $row['myid_last_msg'];
        }
        // Get the counts correct...
        if ($row['approved']) {
            $split1_replies = $row['message_count'] - 1;
            $split1_unapprovedposts = 0;
        } else {
            if (!isset($split1_replies)) {
                $split1_replies = 0;
            } elseif (!$split1_approved) {
                $split1_replies++;
            }
            $split1_unapprovedposts = $row['message_count'];
        }
    }
    $db->free_result($request);
    $split1_firstMem = getMsgMemberID($split1_first_msg);
    $split1_lastMem = getMsgMemberID($split1_last_msg);
    // Find the first and last in the list. (new topic)
    $request = $db->query('', '
		SELECT MIN(id_msg) AS myid_first_msg, MAX(id_msg) AS myid_last_msg, COUNT(*) AS message_count, approved
		FROM {db_prefix}messages
		WHERE id_msg IN ({array_int:msg_list})
			AND id_topic = {int:id_topic}
		GROUP BY id_topic, approved
		ORDER BY approved DESC
		LIMIT 2', array('msg_list' => $splitMessages, 'id_topic' => $split1_ID_TOPIC));
    while ($row = $db->fetch_assoc($request)) {
        // As before get the right first and last message dependant on approved state...
        if (empty($split2_first_msg) || $row['myid_first_msg'] < $split2_first_msg) {
            $split2_first_msg = $row['myid_first_msg'];
        }
        if (empty($split2_last_msg) || $row['approved']) {
            $split2_last_msg = $row['myid_last_msg'];
        }
        // Then do the counts again...
        if ($row['approved']) {
            $split2_approved = true;
            $split2_replies = $row['message_count'] - 1;
            $split2_unapprovedposts = 0;
        } else {
            // Should this one be approved??
            if ($split2_first_msg == $row['myid_first_msg']) {
                $split2_approved = false;
            }
            if (!isset($split2_replies)) {
                $split2_replies = 0;
            } elseif (!$split2_approved) {
                $split2_replies++;
            }
            $split2_unapprovedposts = $row['message_count'];
        }
    }
    $db->free_result($request);
    $split2_firstMem = getMsgMemberID($split2_first_msg);
    $split2_lastMem = getMsgMemberID($split2_last_msg);
    // No database changes yet, so let's double check to see if everything makes at least a little sense.
    if ($split1_first_msg <= 0 || $split1_last_msg <= 0 || $split2_first_msg <= 0 || $split2_last_msg <= 0 || $split1_replies < 0 || $split2_replies < 0 || $split1_unapprovedposts < 0 || $split2_unapprovedposts < 0 || !isset($split1_approved) || !isset($split2_approved)) {
        fatal_lang_error('cant_find_messages');
    }
    // You cannot split off the first message of a topic.
    if ($split1_first_msg > $split2_first_msg) {
        fatal_lang_error('split_first_post', false);
    }
    // We're off to insert the new topic!  Use 0 for now to avoid UNIQUE errors.
    $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', 'num_replies' => 'int', 'unapproved_posts' => 'int', 'approved' => 'int', 'is_sticky' => 'int'), array((int) $id_board, $split2_firstMem, $split2_lastMem, 0, 0, $split2_replies, $split2_unapprovedposts, (int) $split2_approved, 0), array('id_topic'));
    $split2_ID_TOPIC = $db->insert_id('{db_prefix}topics', 'id_topic');
    if ($split2_ID_TOPIC <= 0) {
        fatal_lang_error('cant_insert_topic');
    }
    // Move the messages over to the other topic.
    $new_subject = strtr(Util::htmltrim(Util::htmlspecialchars($new_subject)), array("\r" => '', "\n" => '', "\t" => ''));
    // Check the subject length.
    if (Util::strlen($new_subject) > 100) {
        $new_subject = Util::substr($new_subject, 0, 100);
    }
    // Valid subject?
    if ($new_subject != '') {
        $db->query('', '
			UPDATE {db_prefix}messages
			SET
				id_topic = {int:id_topic},
				subject = CASE WHEN id_msg = {int:split_first_msg} THEN {string:new_subject} ELSE {string:new_subject_replies} END
			WHERE id_msg IN ({array_int:split_msgs})', array('split_msgs' => $splitMessages, 'id_topic' => $split2_ID_TOPIC, 'new_subject' => $new_subject, 'split_first_msg' => $split2_first_msg, 'new_subject_replies' => $txt['response_prefix'] . $new_subject));
        // Cache the new topics subject... we can do it now as all the subjects are the same!
        updateStats('subject', $split2_ID_TOPIC, $new_subject);
    }
    // Any associated reported posts better follow...
    require_once SUBSDIR . '/Topic.subs.php';
    updateSplitTopics(array('splitMessages' => $splitMessages, 'split2_ID_TOPIC' => $split2_ID_TOPIC, 'split1_replies' => $split1_replies, 'split1_first_msg' => $split1_first_msg, 'split1_last_msg' => $split1_last_msg, 'split1_firstMem' => $split1_firstMem, 'split1_lastMem' => $split1_lastMem, 'split1_unapprovedposts' => $split1_unapprovedposts, 'split1_ID_TOPIC' => $split1_ID_TOPIC, 'split2_first_msg' => $split2_first_msg, 'split2_last_msg' => $split2_last_msg, 'split2_ID_TOPIC' => $split2_ID_TOPIC, 'split2_approved' => $split2_approved), $id_board);
    require_once SUBSDIR . '/FollowUps.subs.php';
    // Let's see if we can create a stronger bridge between the two topics
    // @todo not sure what message from the oldest topic I should link to the new one, so I'll go with the first
    linkMessages($split1_first_msg, $split2_ID_TOPIC);
    // Copy log topic entries.
    // @todo This should really be chunked.
    $request = $db->query('', '
		SELECT id_member, id_msg, unwatched
		FROM {db_prefix}log_topics
		WHERE id_topic = {int:id_topic}', array('id_topic' => (int) $split1_ID_TOPIC));
    if ($db->num_rows($request) > 0) {
        $replaceEntries = array();
        while ($row = $db->fetch_assoc($request)) {
            $replaceEntries[] = array($row['id_member'], $split2_ID_TOPIC, $row['id_msg'], $row['unwatched']);
        }
        require_once SUBSDIR . '/Topic.subs.php';
        markTopicsRead($replaceEntries, false);
        unset($replaceEntries);
    }
    $db->free_result($request);
    // Housekeeping.
    updateTopicStats();
    updateLastMessages($id_board);
    logAction('split', array('topic' => $split1_ID_TOPIC, 'new_topic' => $split2_ID_TOPIC, 'board' => $id_board));
    // Notify people that this topic has been split?
    require_once SUBSDIR . '/Notification.subs.php';
    sendNotifications($split1_ID_TOPIC, 'split');
    // If there's a search index that needs updating, update it...
    require_once SUBSDIR . '/Search.subs.php';
    $searchAPI = findSearchAPI();
    if (is_callable(array($searchAPI, 'topicSplit'))) {
        $searchAPI->topicSplit($split2_ID_TOPIC, $splitMessages);
    }
    // Return the ID of the newly created topic.
    return $split2_ID_TOPIC;
}
Exemplo n.º 16
0
function moveTopics($topics, $toBoard)
{
    global $sourcedir, $user_info, $modSettings, $smcFunc;
    // Empty array?
    if (empty($topics)) {
        return;
    } elseif (is_numeric($topics)) {
        $topics = array($topics);
    }
    $num_topics = count($topics);
    $fromBoards = array();
    // Destination board empty or equal to 0?
    if (empty($toBoard)) {
        return;
    }
    // Are we moving to the recycle board?
    $isRecycleDest = !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $toBoard;
    // Determine the source boards...
    $request = $smcFunc['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));
    // Num of rows = 0 -> no topics found. Num of rows > 1 -> topics are on multiple boards.
    if ($smcFunc['db_num_rows']($request) == 0) {
        return;
    }
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        if (!isset($fromBoards[$row['id_board']]['num_posts'])) {
            $fromBoards[$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.
        $fromBoards[$row['id_board']]['num_posts'] += $row['num_replies'] + ($row['approved'] ? $row['num_topics'] : 0);
        $fromBoards[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts'];
        // Add the topics to the right type.
        if ($row['approved']) {
            $fromBoards[$row['id_board']]['num_topics'] += $row['num_topics'];
        } else {
            $fromBoards[$row['id_board']]['unapproved_topics'] += $row['num_topics'];
        }
    }
    $smcFunc['db_free_result']($request);
    // Move over the mark_read data. (because it may be read and now not by some!)
    $SaveAServer = max(0, $modSettings['maxMsgID'] - 50000);
    $request = $smcFunc['db_query']('', '
		SELECT lmr.id_member, lmr.id_msg, t.id_topic
		FROM {db_prefix}topics AS t
			INNER JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board
				AND lmr.id_msg > t.id_first_msg AND lmr.id_msg > {int:protect_lmr_msg})
			LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = lmr.id_member)
		WHERE t.id_topic IN ({array_int:topics})
			AND lmr.id_msg > IFNULL(lt.id_msg, 0)', array('protect_lmr_msg' => $SaveAServer, 'topics' => $topics));
    $log_topics = array();
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        $log_topics[] = array($row['id_topic'], $row['id_member'], $row['id_msg']);
        // Prevent queries from getting too big. Taking some steam off.
        if (count($log_topics) > 500) {
            $smcFunc['db_insert']('replace', '{db_prefix}log_topics', array('id_topic' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), $log_topics, array('id_topic', 'id_member'));
            $log_topics = array();
        }
    }
    $smcFunc['db_free_result']($request);
    // Now that we have all the topics that *should* be marked read, and by which members...
    if (!empty($log_topics)) {
        // Insert that information into the database!
        $smcFunc['db_insert']('replace', '{db_prefix}log_topics', array('id_topic' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), $log_topics, array('id_topic', 'id_member'));
    }
    // Update the number of posts on each board.
    $totalTopics = 0;
    $totalPosts = 0;
    $totalUnapprovedTopics = 0;
    $totalUnapprovedPosts = 0;
    foreach ($fromBoards as $stats) {
        $smcFunc['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']));
        $totalTopics += $stats['num_topics'];
        $totalPosts += $stats['num_posts'];
        $totalUnapprovedTopics += $stats['unapproved_topics'];
        $totalUnapprovedPosts += $stats['unapproved_posts'];
    }
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}boards
		SET
			num_topics = num_topics + {int:total_topics},
			num_posts = num_posts + {int:total_posts},' . ($isRecycleDest ? '
			unapproved_posts = {int:no_unapproved}, unapproved_topics = {int:no_unapproved}' : '
			unapproved_posts = unapproved_posts + {int:total_unapproved_posts},
			unapproved_topics = unapproved_topics + {int:total_unapproved_topics}') . '
		WHERE id_board = {int:id_board}', array('id_board' => $toBoard, 'total_topics' => $totalTopics, 'total_posts' => $totalPosts, 'total_unapproved_topics' => $totalUnapprovedTopics, 'total_unapproved_posts' => $totalUnapprovedPosts, 'no_unapproved' => 0));
    // Move the topic.  Done.  :P
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}topics
		SET id_board = {int:id_board}' . ($isRecycleDest ? ',
			unapproved_posts = {int:no_unapproved}, approved = {int:is_approved}' : '') . '
		WHERE id_topic IN ({array_int:topics})', array('id_board' => $toBoard, 'topics' => $topics, 'is_approved' => 1, 'no_unapproved' => 0));
    // If this was going to the recycle bin, check what messages are being recycled, and remove them from the queue.
    if ($isRecycleDest && ($totalUnapprovedTopics || $totalUnapprovedPosts)) {
        $request = $smcFunc['db_query']('', '
			SELECT id_msg
			FROM {db_prefix}messages
			WHERE id_topic IN ({array_int:topics})
				and approved = {int:not_approved}', array('topics' => $topics, 'not_approved' => 0));
        $approval_msgs = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $approval_msgs[] = $row['id_msg'];
        }
        $smcFunc['db_free_result']($request);
        // Empty the approval queue for these, as we're going to approve them next.
        if (!empty($approval_msgs)) {
            $smcFunc['db_query']('', '
				DELETE FROM {db_prefix}approval_queue
				WHERE id_msg IN ({array_int:message_list})
					AND id_attach = {int:id_attach}', array('message_list' => $approval_msgs, 'id_attach' => 0));
        }
        // Get all the current max and mins.
        $request = $smcFunc['db_query']('', '
			SELECT id_topic, id_first_msg, id_last_msg
			FROM {db_prefix}topics
			WHERE id_topic IN ({array_int:topics})', array('topics' => $topics));
        $topicMaxMin = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $topicMaxMin[$row['id_topic']] = array('min' => $row['id_first_msg'], 'max' => $row['id_last_msg']);
        }
        $smcFunc['db_free_result']($request);
        // Check the MAX and MIN are correct.
        $request = $smcFunc['db_query']('', '
			SELECT id_topic, MIN(id_msg) AS first_msg, MAX(id_msg) AS last_msg
			FROM {db_prefix}messages
			WHERE id_topic IN ({array_int:topics})
			GROUP BY id_topic', array('topics' => $topics));
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            // If not, update.
            if ($row['first_msg'] != $topicMaxMin[$row['id_topic']]['min'] || $row['last_msg'] != $topicMaxMin[$row['id_topic']]['max']) {
                $smcFunc['db_query']('', '
					UPDATE {db_prefix}topics
					SET id_first_msg = {int:first_msg}, id_last_msg = {int:last_msg}
					WHERE id_topic = {int:selected_topic}', array('first_msg' => $row['first_msg'], 'last_msg' => $row['last_msg'], 'selected_topic' => $row['id_topic']));
            }
        }
        $smcFunc['db_free_result']($request);
    }
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}messages
		SET id_board = {int:id_board}' . ($isRecycleDest ? ',approved = {int:is_approved}' : '') . '
		WHERE id_topic IN ({array_int:topics})', array('id_board' => $toBoard, 'topics' => $topics, 'is_approved' => 1));
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}log_reported
		SET id_board = {int:id_board}
		WHERE id_topic IN ({array_int:topics})', array('id_board' => $toBoard, 'topics' => $topics));
    $smcFunc['db_query']('', '
		UPDATE {db_prefix}calendar
		SET id_board = {int:id_board}
		WHERE id_topic IN ({array_int:topics})', array('id_board' => $toBoard, 'topics' => $topics));
    // Mark target board as seen, if it was already marked as seen before.
    $request = $smcFunc['db_query']('', '
		SELECT (IFNULL(lb.id_msg, 0) >= b.id_msg_updated) AS isSeen
		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:id_board}', array('current_member' => $user_info['id'], 'id_board' => $toBoard));
    list($isSeen) = $smcFunc['db_fetch_row']($request);
    $smcFunc['db_free_result']($request);
    if (!empty($isSeen) && !$user_info['is_guest']) {
        $smcFunc['db_insert']('replace', '{db_prefix}log_boards', array('id_board' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), array($toBoard, $user_info['id'], $modSettings['maxMsgID']), array('id_board', 'id_member'));
    }
    // Update 'em pesky stats.
    updateStats('topic');
    updateStats('message');
    updateSettings(array('calendar_updated' => time()));
    // Update the cache?
    if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3) {
        foreach ($topics as $topic_id) {
            cache_put_data('topic_board-' . $topic_id, null, 120);
        }
    }
    require_once $sourcedir . '/Subs-Post.php';
    $updates = array_keys($fromBoards);
    $updates[] = $toBoard;
    updateLastMessages(array_unique($updates));
}
Exemplo n.º 17
0
    /**
     * Set merge options and do the actual merge of two or more topics.
     *
     * the merge options screen:
     * * shows topics to be merged and allows to set some merge options.
     * * is accessed by ?action=mergetopics;sa=options.and can also internally be called by action_quickmod().
     * * uses 'merge_extra_options' sub template of the MergeTopics template.
     *
     * the actual merge:
     * * is accessed with ?action=mergetopics;sa=execute.
     * * updates the statistics to reflect the merge.
     * * logs the action in the moderation log.
     * * sends a notification is sent to all users monitoring this topic.
     * * redirects to ?action=mergetopics;sa=done.
     *
     * @param int[] $topics = array() of topic ids
     */
    public function action_mergeExecute($topics = array())
    {
        global $user_info, $txt, $context, $scripturl, $modSettings;
        $db = database();
        // Check the session.
        checkSession('request');
        require_once SUBSDIR . '/Topic.subs.php';
        require_once SUBSDIR . '/Post.subs.php';
        // Handle URLs from action_mergeIndex.
        if (!empty($_GET['from']) && !empty($_GET['to'])) {
            $topics = array((int) $_GET['from'], (int) $_GET['to']);
        }
        // If we came from a form, the topic IDs came by post.
        if (!empty($_POST['topics']) && is_array($_POST['topics'])) {
            $topics = $_POST['topics'];
        }
        // There's nothing to merge with just one topic...
        if (empty($topics) || !is_array($topics) || count($topics) == 1) {
            fatal_lang_error('merge_need_more_topics');
        }
        // Make sure every topic is numeric, or some nasty things could be done with the DB.
        foreach ($topics as $id => $topic) {
            $topics[$id] = (int) $topic;
        }
        // Joy of all joys, make sure they're not pi**ing about with unapproved topics they can't see :P
        if ($modSettings['postmod_active']) {
            $can_approve_boards = !empty($user_info['mod_cache']['ap']) ? $user_info['mod_cache']['ap'] : boardsAllowedTo('approve_posts');
        }
        // Get info about the topics and polls that will be merged.
        $request = $db->query('', '
			SELECT
				t.id_topic, t.id_board, t.id_poll, t.num_views, t.is_sticky, t.approved, t.num_replies, t.unapproved_posts,
				m1.subject, m1.poster_time AS time_started, IFNULL(mem1.id_member, 0) AS id_member_started, IFNULL(mem1.real_name, m1.poster_name) AS name_started,
				m2.poster_time AS time_updated, IFNULL(mem2.id_member, 0) AS id_member_updated, IFNULL(mem2.real_name, m2.poster_name) AS name_updated
			FROM {db_prefix}topics AS t
				INNER JOIN {db_prefix}messages AS m1 ON (m1.id_msg = t.id_first_msg)
				INNER JOIN {db_prefix}messages AS m2 ON (m2.id_msg = t.id_last_msg)
				LEFT JOIN {db_prefix}members AS mem1 ON (mem1.id_member = m1.id_member)
				LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = m2.id_member)
			WHERE t.id_topic IN ({array_int:topic_list})
			ORDER BY t.id_first_msg
			LIMIT ' . count($topics), array('topic_list' => $topics));
        if ($db->num_rows($request) < 2) {
            fatal_lang_error('no_topic_id');
        }
        $num_views = 0;
        $is_sticky = 0;
        $boardTotals = array();
        $topic_data = array();
        $boards = array();
        $polls = array();
        $firstTopic = 0;
        while ($row = $db->fetch_assoc($request)) {
            // Make a note for the board counts...
            if (!isset($boardTotals[$row['id_board']])) {
                $boardTotals[$row['id_board']] = array('num_posts' => 0, 'num_topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0);
            }
            // We can't see unapproved topics here?
            if ($modSettings['postmod_active'] && !$row['approved'] && $can_approve_boards != array(0) && in_array($row['id_board'], $can_approve_boards)) {
                continue;
            } elseif (!$row['approved']) {
                $boardTotals[$row['id_board']]['unapproved_topics']++;
            } else {
                $boardTotals[$row['id_board']]['num_topics']++;
            }
            $boardTotals[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts'];
            $boardTotals[$row['id_board']]['num_posts'] += $row['num_replies'] + ($row['approved'] ? 1 : 0);
            $topic_data[$row['id_topic']] = array('id' => $row['id_topic'], 'board' => $row['id_board'], 'poll' => $row['id_poll'], 'num_views' => $row['num_views'], 'subject' => $row['subject'], 'started' => array('time' => standardTime($row['time_started']), 'html_time' => htmlTime($row['time_started']), 'timestamp' => forum_time(true, $row['time_started']), 'href' => empty($row['id_member_started']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_started'], 'link' => empty($row['id_member_started']) ? $row['name_started'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_started'] . '">' . $row['name_started'] . '</a>'), 'updated' => array('time' => standardTime($row['time_updated']), 'html_time' => htmlTime($row['time_updated']), 'timestamp' => forum_time(true, $row['time_updated']), 'href' => empty($row['id_member_updated']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_updated'], 'link' => empty($row['id_member_updated']) ? $row['name_updated'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_updated'] . '">' . $row['name_updated'] . '</a>'));
            $num_views += $row['num_views'];
            $boards[] = $row['id_board'];
            // If there's no poll, id_poll == 0...
            if ($row['id_poll'] > 0) {
                $polls[] = $row['id_poll'];
            }
            // Store the id_topic with the lowest id_first_msg.
            if (empty($firstTopic)) {
                $firstTopic = $row['id_topic'];
            }
            $is_sticky = max($is_sticky, $row['is_sticky']);
        }
        $db->free_result($request);
        // If we didn't get any topics then they've been messing with unapproved stuff.
        if (empty($topic_data)) {
            fatal_lang_error('no_topic_id');
        }
        $boards = array_values(array_unique($boards));
        // The parameters of action_mergeExecute were set, so this must've been an internal call.
        if (!empty($topics)) {
            isAllowedTo('merge_any', $boards);
            loadTemplate('MergeTopics');
        }
        // Get the boards a user is allowed to merge in.
        $merge_boards = boardsAllowedTo('merge_any');
        if (empty($merge_boards)) {
            fatal_lang_error('cannot_merge_any', 'user');
        }
        require_once SUBSDIR . '/Boards.subs.php';
        // Make sure they can see all boards....
        $query_boards = array('boards' => $boards);
        if (!in_array(0, $merge_boards)) {
            $query_boards['boards'] = array_merge($query_boards['boards'], $merge_boards);
        }
        // Saved in a variable to (potentially) save a query later
        $boards_info = fetchBoardsInfo($query_boards);
        // This happens when a member is moderator of a board he cannot see
        foreach ($boards as $board) {
            if (!isset($boards_info[$board])) {
                fatal_lang_error('no_board');
            }
        }
        if (empty($_REQUEST['sa']) || $_REQUEST['sa'] == 'options') {
            if (count($polls) > 1) {
                $request = $db->query('', '
					SELECT t.id_topic, t.id_poll, m.subject, p.question
					FROM {db_prefix}polls AS p
						INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll)
						INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
					WHERE p.id_poll IN ({array_int:polls})
					LIMIT ' . count($polls), array('polls' => $polls));
                while ($row = $db->fetch_assoc($request)) {
                    $context['polls'][] = array('id' => $row['id_poll'], 'topic' => array('id' => $row['id_topic'], 'subject' => $row['subject']), 'question' => $row['question'], 'selected' => $row['id_topic'] == $firstTopic);
                }
                $db->free_result($request);
            }
            if (count($boards) > 1) {
                foreach ($boards_info as $row) {
                    $context['boards'][] = array('id' => $row['id_board'], 'name' => $row['name'], 'selected' => $row['id_board'] == $topic_data[$firstTopic]['board']);
                }
            }
            $context['topics'] = $topic_data;
            foreach ($topic_data as $id => $topic) {
                $context['topics'][$id]['selected'] = $topic['id'] == $firstTopic;
            }
            $context['page_title'] = $txt['merge'];
            $context['sub_template'] = 'merge_extra_options';
            return;
        }
        // Determine target board.
        $target_board = count($boards) > 1 ? (int) $_REQUEST['board'] : $boards[0];
        if (!in_array($target_board, $boards)) {
            fatal_lang_error('no_board');
        }
        // Determine which poll will survive and which polls won't.
        $target_poll = count($polls) > 1 ? (int) $_POST['poll'] : (count($polls) == 1 ? $polls[0] : 0);
        if ($target_poll > 0 && !in_array($target_poll, $polls)) {
            fatal_lang_error('no_access', false);
        }
        $deleted_polls = empty($target_poll) ? $polls : array_diff($polls, array($target_poll));
        // Determine the subject of the newly merged topic - was a custom subject specified?
        if (empty($_POST['subject']) && isset($_POST['custom_subject']) && $_POST['custom_subject'] != '') {
            $target_subject = strtr(Util::htmltrim(Util::htmlspecialchars($_POST['custom_subject'])), array("\r" => '', "\n" => '', "\t" => ''));
            // Keep checking the length.
            if (Util::strlen($target_subject) > 100) {
                $target_subject = Util::substr($target_subject, 0, 100);
            }
            // Nothing left - odd but pick the first topics subject.
            if ($target_subject == '') {
                $target_subject = $topic_data[$firstTopic]['subject'];
            }
        } elseif (!empty($topic_data[(int) $_POST['subject']]['subject'])) {
            $target_subject = $topic_data[(int) $_POST['subject']]['subject'];
        } else {
            $target_subject = $topic_data[$firstTopic]['subject'];
        }
        // Get the first and last message and the number of messages....
        $request = $db->query('', '
			SELECT approved, MIN(id_msg) AS first_msg, MAX(id_msg) AS last_msg, COUNT(*) AS message_count
			FROM {db_prefix}messages
			WHERE id_topic IN ({array_int:topics})
			GROUP BY approved
			ORDER BY approved DESC', array('topics' => $topics));
        $topic_approved = 1;
        $first_msg = 0;
        while ($row = $db->fetch_assoc($request)) {
            // If this is approved, or is fully unapproved.
            if ($row['approved'] || !isset($first_msg)) {
                $first_msg = $row['first_msg'];
                $last_msg = $row['last_msg'];
                if ($row['approved']) {
                    $num_replies = $row['message_count'] - 1;
                    $num_unapproved = 0;
                } else {
                    $topic_approved = 0;
                    $num_replies = 0;
                    $num_unapproved = $row['message_count'];
                }
            } else {
                // If this has a lower first_msg then the first post is not approved and hence the number of replies was wrong!
                if ($first_msg > $row['first_msg']) {
                    $first_msg = $row['first_msg'];
                    $num_replies++;
                    $topic_approved = 0;
                }
                $num_unapproved = $row['message_count'];
            }
        }
        $db->free_result($request);
        // Ensure we have a board stat for the target board.
        if (!isset($boardTotals[$target_board])) {
            $boardTotals[$target_board] = array('num_posts' => 0, 'num_topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0);
        }
        // Fix the topic count stuff depending on what the new one counts as.
        if ($topic_approved) {
            $boardTotals[$target_board]['num_topics']--;
        } else {
            $boardTotals[$target_board]['unapproved_topics']--;
        }
        $boardTotals[$target_board]['unapproved_posts'] -= $num_unapproved;
        $boardTotals[$target_board]['num_posts'] -= $topic_approved ? $num_replies + 1 : $num_replies;
        // Get the member ID of the first and last message.
        $request = $db->query('', '
			SELECT id_member
			FROM {db_prefix}messages
			WHERE id_msg IN ({int:first_msg}, {int:last_msg})
			ORDER BY id_msg
			LIMIT 2', array('first_msg' => $first_msg, 'last_msg' => $last_msg));
        list($member_started) = $db->fetch_row($request);
        list($member_updated) = $db->fetch_row($request);
        // First and last message are the same, so only row was returned.
        if ($member_updated === null) {
            $member_updated = $member_started;
        }
        $db->free_result($request);
        // Obtain all the message ids we are going to affect.
        $affected_msgs = messagesInTopics($topics);
        // Assign the first topic ID to be the merged topic.
        $id_topic = min($topics);
        // Grab the response prefix (like 'Re: ') in the default forum language.
        $context['response_prefix'] = response_prefix();
        $enforce_subject = isset($_POST['enforce_subject']) ? Util::htmlspecialchars(trim($_POST['enforce_subject'])) : '';
        // Merge topic notifications.
        $notifications = isset($_POST['notifications']) && is_array($_POST['notifications']) ? array_intersect($topics, $_POST['notifications']) : array();
        fixMergedTopics($first_msg, $topics, $id_topic, $target_board, $target_subject, $enforce_subject, $notifications);
        // Asssign the properties of the newly merged topic.
        $db->query('', '
			UPDATE {db_prefix}topics
			SET
				id_board = {int:id_board},
				id_member_started = {int:id_member_started},
				id_member_updated = {int:id_member_updated},
				id_first_msg = {int:id_first_msg},
				id_last_msg = {int:id_last_msg},
				id_poll = {int:id_poll},
				num_replies = {int:num_replies},
				unapproved_posts = {int:unapproved_posts},
				num_views = {int:num_views},
				is_sticky = {int:is_sticky},
				approved = {int:approved}
			WHERE id_topic = {int:id_topic}', array('id_board' => $target_board, 'is_sticky' => $is_sticky, 'approved' => $topic_approved, 'id_topic' => $id_topic, 'id_member_started' => $member_started, 'id_member_updated' => $member_updated, 'id_first_msg' => $first_msg, 'id_last_msg' => $last_msg, 'id_poll' => $target_poll, 'num_replies' => $num_replies, 'unapproved_posts' => $num_unapproved, 'num_views' => $num_views));
        // Get rid of the redundant polls.
        if (!empty($deleted_polls)) {
            require_once SUBSDIR . '/Poll.subs.php';
            removePoll($deleted_polls);
        }
        // Cycle through each board...
        foreach ($boardTotals as $id_board => $stats) {
            decrementBoard($id_board, $stats);
        }
        // Determine the board the final topic resides in
        $topic_info = getTopicInfo($id_topic);
        $id_board = $topic_info['id_board'];
        // Update all the statistics.
        updateStats('topic');
        updateStats('subject', $id_topic, $target_subject);
        updateLastMessages($boards);
        logAction('merge', array('topic' => $id_topic, 'board' => $id_board));
        // Notify people that these topics have been merged?
        require_once SUBSDIR . '/Notification.subs.php';
        sendNotifications($id_topic, 'merge');
        // If there's a search index that needs updating, update it...
        require_once SUBSDIR . '/Search.subs.php';
        $searchAPI = findSearchAPI();
        if (is_callable(array($searchAPI, 'topicMerge'))) {
            $searchAPI->topicMerge($id_topic, $topics, $affected_msgs, empty($enforce_subject) ? null : array($context['response_prefix'], $target_subject));
        }
        // Send them to the all done page.
        redirectexit('action=mergetopics;sa=done;to=' . $id_topic . ';targetboard=' . $target_board);
    }