Beispiel #1
0
function UnreadTopics()
{
    global $board, $txt, $scripturl, $sourcedir;
    global $user_info, $context, $settings, $modSettings, $smcFunc, $options;
    // Guests can't have unread things, we don't know anything about them.
    is_not_guest();
    // Prefetching + lots of MySQL work = bad mojo.
    if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') {
        ob_end_clean();
        header('HTTP/1.1 403 Forbidden');
        die;
    }
    $context['showing_all_topics'] = isset($_GET['all']);
    $context['start'] = (int) $_REQUEST['start'];
    $context['topics_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page']) && !WIRELESS ? $options['topics_per_page'] : $modSettings['defaultMaxTopics'];
    if ($_REQUEST['action'] == 'unread') {
        $context['page_title'] = $context['showing_all_topics'] ? $txt['unread_topics_all'] : $txt['unread_topics_visit'];
    } else {
        $context['page_title'] = $txt['unread_replies'];
    }
    if ($context['showing_all_topics'] && !empty($context['load_average']) && !empty($modSettings['loadavg_allunread']) && $context['load_average'] >= $modSettings['loadavg_allunread']) {
        fatal_lang_error('loadavg_allunread_disabled', false);
    } elseif ($_REQUEST['action'] != 'unread' && !empty($context['load_average']) && !empty($modSettings['loadavg_unreadreplies']) && $context['load_average'] >= $modSettings['loadavg_unreadreplies']) {
        fatal_lang_error('loadavg_unreadreplies_disabled', false);
    } elseif (!$context['showing_all_topics'] && $_REQUEST['action'] == 'unread' && !empty($context['load_average']) && !empty($modSettings['loadavg_unread']) && $context['load_average'] >= $modSettings['loadavg_unread']) {
        fatal_lang_error('loadavg_unread_disabled', false);
    }
    // Parameters for the main query.
    $query_parameters = array();
    // Are we specifying any specific board?
    if (isset($_REQUEST['children']) && (!empty($board) || !empty($_REQUEST['boards']))) {
        $boards = array();
        if (!empty($_REQUEST['boards'])) {
            $_REQUEST['boards'] = explode(',', $_REQUEST['boards']);
            foreach ($_REQUEST['boards'] as $b) {
                $boards[] = (int) $b;
            }
        }
        if (!empty($board)) {
            $boards[] = (int) $board;
        }
        // The easiest thing is to just get all the boards they can see, but since we've specified the top of tree we ignore some of them
        $request = $smcFunc['db_query']('', '
			SELECT b.id_board, b.id_parent
			FROM {db_prefix}boards AS b
			WHERE {query_wanna_see_board}
				AND b.child_level > {int:no_child}
				AND b.id_board NOT IN ({array_int:boards})
			ORDER BY child_level ASC
			', array('no_child' => 0, 'boards' => $boards));
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            if (in_array($row['id_parent'], $boards)) {
                $boards[] = $row['id_board'];
            }
        }
        $smcFunc['db_free_result']($request);
        if (empty($boards)) {
            fatal_lang_error('error_no_boards_selected');
        }
        $query_this_board = 'id_board IN ({array_int:boards})';
        $query_parameters['boards'] = $boards;
        $context['querystring_board_limits'] = ';boards=' . implode(',', $boards) . ';start=%d';
    } elseif (!empty($board)) {
        $query_this_board = 'id_board = {int:board}';
        $query_parameters['board'] = $board;
        $context['querystring_board_limits'] = ';board=' . $board . '.%1$d';
    } elseif (!empty($_REQUEST['boards'])) {
        $_REQUEST['boards'] = explode(',', $_REQUEST['boards']);
        foreach ($_REQUEST['boards'] as $i => $b) {
            $_REQUEST['boards'][$i] = (int) $b;
        }
        $request = $smcFunc['db_query']('', '
			SELECT b.id_board
			FROM {db_prefix}boards AS b
			WHERE {query_see_board}
				AND b.id_board IN ({array_int:board_list})', array('board_list' => $_REQUEST['boards']));
        $boards = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $boards[] = $row['id_board'];
        }
        $smcFunc['db_free_result']($request);
        if (empty($boards)) {
            fatal_lang_error('error_no_boards_selected');
        }
        $query_this_board = 'id_board IN ({array_int:boards})';
        $query_parameters['boards'] = $boards;
        $context['querystring_board_limits'] = ';boards=' . implode(',', $boards) . ';start=%1$d';
    } elseif (!empty($_REQUEST['c'])) {
        $_REQUEST['c'] = explode(',', $_REQUEST['c']);
        foreach ($_REQUEST['c'] as $i => $c) {
            $_REQUEST['c'][$i] = (int) $c;
        }
        $see_board = isset($_REQUEST['action']) && $_REQUEST['action'] == 'unreadreplies' ? 'query_see_board' : 'query_wanna_see_board';
        $request = $smcFunc['db_query']('', '
			SELECT b.id_board
			FROM {db_prefix}boards AS b
			WHERE ' . $user_info[$see_board] . '
				AND b.id_cat IN ({array_int:id_cat})', array('id_cat' => $_REQUEST['c']));
        $boards = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $boards[] = $row['id_board'];
        }
        $smcFunc['db_free_result']($request);
        if (empty($boards)) {
            fatal_lang_error('error_no_boards_selected');
        }
        $query_this_board = 'id_board IN ({array_int:boards})';
        $query_parameters['boards'] = $boards;
        $context['querystring_board_limits'] = ';c=' . implode(',', $_REQUEST['c']) . ';start=%1$d';
    } else {
        $see_board = isset($_REQUEST['action']) && $_REQUEST['action'] == 'unreadreplies' ? 'query_see_board' : 'query_wanna_see_board';
        // Don't bother to show deleted posts!
        $request = $smcFunc['db_query']('', '
			SELECT b.id_board
			FROM {db_prefix}boards AS b
			WHERE ' . $user_info[$see_board] . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
				AND b.id_board != {int:recycle_board}' : ''), array('recycle_board' => (int) $modSettings['recycle_board']));
        $boards = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $boards[] = $row['id_board'];
        }
        $smcFunc['db_free_result']($request);
        if (empty($boards)) {
            fatal_lang_error('error_no_boards_selected');
        }
        $query_this_board = 'id_board IN ({array_int:boards})';
        $query_parameters['boards'] = $boards;
        $context['querystring_board_limits'] = ';start=%1$d';
        $context['no_board_limits'] = true;
    }
    $sort_methods = array('subject' => 'ms.subject', 'starter' => 'IFNULL(mems.real_name, ms.poster_name)', 'replies' => 't.num_replies', 'views' => 't.num_views', 'first_post' => 't.id_topic', 'last_post' => 't.id_last_msg');
    // The default is the most logical: newest first.
    if (!isset($_REQUEST['sort']) || !isset($sort_methods[$_REQUEST['sort']])) {
        $context['sort_by'] = 'last_post';
        $_REQUEST['sort'] = 't.id_last_msg';
        $ascending = isset($_REQUEST['asc']);
        $context['querystring_sort_limits'] = $ascending ? ';asc' : '';
    } else {
        $context['sort_by'] = $_REQUEST['sort'];
        $_REQUEST['sort'] = $sort_methods[$_REQUEST['sort']];
        $ascending = !isset($_REQUEST['desc']);
        $context['querystring_sort_limits'] = ';sort=' . $context['sort_by'] . ($ascending ? '' : ';desc');
    }
    $context['sort_direction'] = $ascending ? 'up' : 'down';
    if (!empty($_REQUEST['c']) && is_array($_REQUEST['c']) && count($_REQUEST['c']) == 1) {
        $request = $smcFunc['db_query']('', '
			SELECT name
			FROM {db_prefix}categories
			WHERE id_cat = {int:id_cat}
			LIMIT 1', array('id_cat' => (int) $_REQUEST['c'][0]));
        list($name) = $smcFunc['db_fetch_row']($request);
        $smcFunc['db_free_result']($request);
        $context['linktree'][] = array('url' => $scripturl . '#c' . (int) $_REQUEST['c'][0], 'name' => $name);
    }
    $context['linktree'][] = array('url' => $scripturl . '?action=' . $_REQUEST['action'] . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'], 'name' => $_REQUEST['action'] == 'unread' ? $txt['unread_topics_visit'] : $txt['unread_replies']);
    if ($context['showing_all_topics']) {
        $context['linktree'][] = array('url' => $scripturl . '?action=' . $_REQUEST['action'] . ';all' . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'], 'name' => $txt['unread_topics_all']);
    } else {
        $txt['unread_topics_visit_none'] = strtr($txt['unread_topics_visit_none'], array('?action=unread;all' => '?action=unread;all' . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits']));
    }
    if (WIRELESS) {
        $context['sub_template'] = WIRELESS_PROTOCOL . '_recent';
    } else {
        loadTemplate('Recent');
        $context['sub_template'] = $_REQUEST['action'] == 'unread' ? 'unread' : 'replies';
    }
    // Setup the default topic icons... for checking they exist and the like ;)
    $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless', 'clip');
    $context['icon_sources'] = array();
    foreach ($stable_icons as $icon) {
        $context['icon_sources'][$icon] = 'images_url';
    }
    $is_topics = $_REQUEST['action'] == 'unread';
    // This part is the same for each query.
    $select_clause = '
				ms.subject AS first_subject, ms.poster_time AS first_poster_time, ms.id_topic, t.id_board, b.name AS bname,
				t.num_replies, t.num_views, ms.id_member AS id_first_member, ml.id_member AS id_last_member,
				ml.poster_time AS last_poster_time, IFNULL(mems.real_name, ms.poster_name) AS first_poster_name,
				IFNULL(meml.real_name, ml.poster_name) AS last_poster_name, ml.subject AS last_subject,
				ml.icon AS last_icon, ms.icon AS first_icon, t.id_poll, t.is_sticky, t.locked, ml.modified_time AS last_modified_time,
				IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from, SUBSTRING(ml.body, 1, 385) AS last_body,
				SUBSTRING(ms.body, 1, 385) AS first_body, ml.smileys_enabled AS last_smileys, ms.smileys_enabled AS first_smileys, t.id_first_msg, t.id_last_msg';
    if ($context['showing_all_topics']) {
        if (!empty($board)) {
            $request = $smcFunc['db_query']('', '
				SELECT MIN(id_msg)
				FROM {db_prefix}log_mark_read
				WHERE id_member = {int:current_member}
					AND id_board = {int:current_board}', array('current_board' => $board, 'current_member' => $user_info['id']));
            list($earliest_msg) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
        } else {
            $request = $smcFunc['db_query']('', '
				SELECT MIN(lmr.id_msg)
				FROM {db_prefix}boards AS b
					LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = b.id_board AND lmr.id_member = {int:current_member})
				WHERE {query_see_board}', array('current_member' => $user_info['id']));
            list($earliest_msg) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
        }
        // This is needed in case of topics marked unread.
        if (empty($earliest_msg)) {
            $earliest_msg = 0;
        } else {
            // Using caching, when possible, to ignore the below slow query.
            if (isset($_SESSION['cached_log_time']) && $_SESSION['cached_log_time'][0] + 45 > time()) {
                $earliest_msg2 = $_SESSION['cached_log_time'][1];
            } else {
                // This query is pretty slow, but it's needed to ensure nothing crucial is ignored.
                $request = $smcFunc['db_query']('', '
					SELECT MIN(id_msg)
					FROM {db_prefix}log_topics
					WHERE id_member = {int:current_member}', array('current_member' => $user_info['id']));
                list($earliest_msg2) = $smcFunc['db_fetch_row']($request);
                $smcFunc['db_free_result']($request);
                // In theory this could be zero, if the first ever post is unread, so fudge it ;)
                if ($earliest_msg2 == 0) {
                    $earliest_msg2 = -1;
                }
                $_SESSION['cached_log_time'] = array(time(), $earliest_msg2);
            }
            $earliest_msg = min($earliest_msg2, $earliest_msg);
        }
    }
    // !!! Add modified_time in for log_time check?
    if ($modSettings['totalMessages'] > 100000 && $context['showing_all_topics']) {
        $smcFunc['db_query']('', '
			DROP TABLE IF EXISTS {db_prefix}log_topics_unread', array());
        // Let's copy things out of the log_topics table, to reduce searching.
        $have_temp_table = $smcFunc['db_query']('', '
			CREATE TEMPORARY TABLE {db_prefix}log_topics_unread (
				PRIMARY KEY (id_topic)
			)
			SELECT lt.id_topic, lt.id_msg
			FROM {db_prefix}topics AS t
				INNER JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic)
			WHERE lt.id_member = {int:current_member}
				AND t.' . $query_this_board . (empty($earliest_msg) ? '' : '
				AND t.id_last_msg > {int:earliest_msg}') . ($modSettings['postmod_active'] ? '
				AND t.approved = {int:is_approved}' : ''), array_merge($query_parameters, array('current_member' => $user_info['id'], 'earliest_msg' => !empty($earliest_msg) ? $earliest_msg : 0, 'is_approved' => 1, 'db_error_skip' => true))) !== false;
    } else {
        $have_temp_table = false;
    }
    if ($context['showing_all_topics'] && $have_temp_table) {
        $request = $smcFunc['db_query']('', '
			SELECT COUNT(*), MIN(t.id_last_msg)
			FROM {db_prefix}topics AS t
				LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic)
				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
			WHERE t.' . $query_this_board . (!empty($earliest_msg) ? '
				AND t.id_last_msg > {int:earliest_msg}' : '') . '
				AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? '
				AND t.approved = {int:is_approved}' : ''), array_merge($query_parameters, array('current_member' => $user_info['id'], 'earliest_msg' => !empty($earliest_msg) ? $earliest_msg : 0, 'is_approved' => 1)));
        list($num_topics, $min_message) = $smcFunc['db_fetch_row']($request);
        $smcFunc['db_free_result']($request);
        // Make sure the starting place makes sense and construct the page index.
        $context['page_index'] = constructPageIndex($scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . $context['querystring_board_limits'] . $context['querystring_sort_limits'], $_REQUEST['start'], $num_topics, $context['topics_per_page'], true);
        $context['current_page'] = (int) $_REQUEST['start'] / $context['topics_per_page'];
        $context['links'] = array('first' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'] : '', 'prev' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] - $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'next' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] + $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'last' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], floor(($num_topics - 1) / $context['topics_per_page']) * $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'up' => $scripturl);
        $context['page_info'] = array('current_page' => $_REQUEST['start'] / $context['topics_per_page'] + 1, 'num_pages' => floor(($num_topics - 1) / $context['topics_per_page']) + 1);
        if ($num_topics == 0) {
            // Mark the boards as read if there are no unread topics!
            require_once $sourcedir . '/Subs-Boards.php';
            markBoardsRead(empty($boards) ? $board : $boards);
            $context['topics'] = array();
            if ($context['querystring_board_limits'] == ';start=%1$d') {
                $context['querystring_board_limits'] = '';
            } else {
                $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']);
            }
            return;
        } else {
            $min_message = (int) $min_message;
        }
        $request = $smcFunc['db_query']('substring', '
			SELECT ' . $select_clause . '
			FROM {db_prefix}messages AS ms
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ms.id_topic AND t.id_first_msg = ms.id_msg)
				INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
				LEFT JOIN {db_prefix}boards AS b ON (b.id_board = ms.id_board)
				LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)
				LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)
				LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic)
				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
			WHERE b.' . $query_this_board . '
				AND t.id_last_msg >= {int:min_message}
				AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? '
				AND ms.approved = {int:is_approved}' : '') . '
			ORDER BY {raw:sort}
			LIMIT {int:offset}, {int:limit}', array_merge($query_parameters, array('current_member' => $user_info['id'], 'min_message' => $min_message, 'is_approved' => 1, 'sort' => $_REQUEST['sort'] . ($ascending ? '' : ' DESC'), 'offset' => $_REQUEST['start'], 'limit' => $context['topics_per_page'])));
    } elseif ($is_topics) {
        $request = $smcFunc['db_query']('', '
			SELECT COUNT(*), MIN(t.id_last_msg)
			FROM {db_prefix}topics AS t' . (!empty($have_temp_table) ? '
				LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic)' : '
				LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})') . '
				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
			WHERE t.' . $query_this_board . ($context['showing_all_topics'] && !empty($earliest_msg) ? '
				AND t.id_last_msg > {int:earliest_msg}' : (!$context['showing_all_topics'] && empty($_SESSION['first_login']) ? '
				AND t.id_last_msg > {int:id_msg_last_visit}' : '')) . '
				AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? '
				AND t.approved = {int:is_approved}' : ''), array_merge($query_parameters, array('current_member' => $user_info['id'], 'earliest_msg' => !empty($earliest_msg) ? $earliest_msg : 0, 'id_msg_last_visit' => $_SESSION['id_msg_last_visit'], 'is_approved' => 1)));
        list($num_topics, $min_message) = $smcFunc['db_fetch_row']($request);
        $smcFunc['db_free_result']($request);
        // Make sure the starting place makes sense and construct the page index.
        $context['page_index'] = constructPageIndex($scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . $context['querystring_board_limits'] . $context['querystring_sort_limits'], $_REQUEST['start'], $num_topics, $context['topics_per_page'], true);
        $context['current_page'] = (int) $_REQUEST['start'] / $context['topics_per_page'];
        $context['links'] = array('first' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'] : '', 'prev' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] - $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'next' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] + $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'last' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], floor(($num_topics - 1) / $context['topics_per_page']) * $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'up' => $scripturl);
        $context['page_info'] = array('current_page' => $_REQUEST['start'] / $context['topics_per_page'] + 1, 'num_pages' => floor(($num_topics - 1) / $context['topics_per_page']) + 1);
        if ($num_topics == 0) {
            // Is this an all topics query?
            if ($context['showing_all_topics']) {
                // Since there are no unread topics, mark the boards as read!
                require_once $sourcedir . '/Subs-Boards.php';
                markBoardsRead(empty($boards) ? $board : $boards);
            }
            $context['topics'] = array();
            if ($context['querystring_board_limits'] == ';start=%d') {
                $context['querystring_board_limits'] = '';
            } else {
                $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']);
            }
            return;
        } else {
            $min_message = (int) $min_message;
        }
        $request = $smcFunc['db_query']('substring', '
			SELECT ' . $select_clause . '
			FROM {db_prefix}messages AS ms
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ms.id_topic AND t.id_first_msg = ms.id_msg)
				INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
				LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
				LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)
				LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)' . (!empty($have_temp_table) ? '
				LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic)' : '
				LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})') . '
				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
			WHERE t.' . $query_this_board . '
				AND t.id_last_msg >= {int:min_message}
				AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < ml.id_msg' . ($modSettings['postmod_active'] ? '
				AND ms.approved = {int:is_approved}' : '') . '
			ORDER BY {raw:order}
			LIMIT {int:offset}, {int:limit}', array_merge($query_parameters, array('current_member' => $user_info['id'], 'min_message' => $min_message, 'is_approved' => 1, 'order' => $_REQUEST['sort'] . ($ascending ? '' : ' DESC'), 'offset' => $_REQUEST['start'], 'limit' => $context['topics_per_page'])));
    } else {
        if ($modSettings['totalMessages'] > 100000) {
            $smcFunc['db_query']('', '
				DROP TABLE IF EXISTS {db_prefix}topics_posted_in', array());
            $smcFunc['db_query']('', '
				DROP TABLE IF EXISTS {db_prefix}log_topics_posted_in', array());
            $sortKey_joins = array('ms.subject' => '
					INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)', 'IFNULL(mems.real_name, ms.poster_name)' => '
					INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)
					LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)');
            // The main benefit of this temporary table is not that it's faster; it's that it avoids locks later.
            $have_temp_table = $smcFunc['db_query']('', '
				CREATE TEMPORARY TABLE {db_prefix}topics_posted_in (
					id_topic mediumint(8) unsigned NOT NULL default {string:string_zero},
					id_board smallint(5) unsigned NOT NULL default {string:string_zero},
					id_last_msg int(10) unsigned NOT NULL default {string:string_zero},
					id_msg int(10) unsigned NOT NULL default {string:string_zero},
					PRIMARY KEY (id_topic)
				)
				SELECT t.id_topic, t.id_board, t.id_last_msg, IFNULL(lmr.id_msg, 0) AS id_msg' . (!in_array($_REQUEST['sort'], array('t.id_last_msg', 't.id_topic')) ? ', ' . $_REQUEST['sort'] . ' AS sort_key' : '') . '
				FROM {db_prefix}messages AS m
					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
					LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})' . (isset($sortKey_joins[$_REQUEST['sort']]) ? $sortKey_joins[$_REQUEST['sort']] : '') . '
				WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
					AND t.id_board = {int:current_board}' : '') . ($modSettings['postmod_active'] ? '
					AND t.approved = {int:is_approved}' : '') . '
				GROUP BY m.id_topic', array('current_board' => $board, 'current_member' => $user_info['id'], 'is_approved' => 1, 'string_zero' => '0', 'db_error_skip' => true)) !== false;
            // If that worked, create a sample of the log_topics table too.
            if ($have_temp_table) {
                $have_temp_table = $smcFunc['db_query']('', '
					CREATE TEMPORARY TABLE {db_prefix}log_topics_posted_in (
						PRIMARY KEY (id_topic)
					)
					SELECT lt.id_topic, lt.id_msg
					FROM {db_prefix}log_topics AS lt
						INNER JOIN {db_prefix}topics_posted_in AS pi ON (pi.id_topic = lt.id_topic)
					WHERE lt.id_member = {int:current_member}', array('current_member' => $user_info['id'], 'db_error_skip' => true)) !== false;
            }
        }
        if (!empty($have_temp_table)) {
            $request = $smcFunc['db_query']('', '
				SELECT COUNT(*)
				FROM {db_prefix}topics_posted_in AS pi
					LEFT JOIN {db_prefix}log_topics_posted_in AS lt ON (lt.id_topic = pi.id_topic)
				WHERE pi.' . $query_this_board . '
					AND IFNULL(lt.id_msg, pi.id_msg) < pi.id_last_msg', array_merge($query_parameters, array()));
            list($num_topics) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
        } else {
            $request = $smcFunc['db_query']('unread_fetch_topic_count', '
				SELECT COUNT(DISTINCT t.id_topic), MIN(t.id_last_msg)
				FROM {db_prefix}topics AS t
					INNER JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic)
					LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
					LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
				WHERE t.' . $query_this_board . '
					AND m.id_member = {int:current_member}
					AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? '
					AND t.approved = {int:is_approved}' : ''), array_merge($query_parameters, array('current_member' => $user_info['id'], 'is_approved' => 1)));
            list($num_topics, $min_message) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
        }
        // Make sure the starting place makes sense and construct the page index.
        $context['page_index'] = constructPageIndex($scripturl . '?action=' . $_REQUEST['action'] . $context['querystring_board_limits'] . $context['querystring_sort_limits'], $_REQUEST['start'], $num_topics, $context['topics_per_page'], true);
        $context['current_page'] = (int) $_REQUEST['start'] / $context['topics_per_page'];
        $context['links'] = array('first' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'] : '', 'prev' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] - $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'next' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] + $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'last' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], floor(($num_topics - 1) / $context['topics_per_page']) * $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'up' => $scripturl);
        $context['page_info'] = array('current_page' => $_REQUEST['start'] / $context['topics_per_page'] + 1, 'num_pages' => floor(($num_topics - 1) / $context['topics_per_page']) + 1);
        if ($num_topics == 0) {
            $context['topics'] = array();
            if ($context['querystring_board_limits'] == ';start=%d') {
                $context['querystring_board_limits'] = '';
            } else {
                $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']);
            }
            return;
        }
        if (!empty($have_temp_table)) {
            $request = $smcFunc['db_query']('', '
				SELECT t.id_topic
				FROM {db_prefix}topics_posted_in AS t
					LEFT JOIN {db_prefix}log_topics_posted_in AS lt ON (lt.id_topic = t.id_topic)
				WHERE t.' . $query_this_board . '
					AND IFNULL(lt.id_msg, t.id_msg) < t.id_last_msg
				ORDER BY {raw:order}
				LIMIT {int:offset}, {int:limit}', array_merge($query_parameters, array('order' => (in_array($_REQUEST['sort'], array('t.id_last_msg', 't.id_topic')) ? $_REQUEST['sort'] : 't.sort_key') . ($ascending ? '' : ' DESC'), 'offset' => $_REQUEST['start'], 'limit' => $context['topics_per_page'])));
        } else {
            $request = $smcFunc['db_query']('unread_replies', '
				SELECT DISTINCT t.id_topic
				FROM {db_prefix}topics AS t
					INNER JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic AND m.id_member = {int:current_member})' . (strpos($_REQUEST['sort'], 'ms.') === false ? '' : '
					INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)') . (strpos($_REQUEST['sort'], 'mems.') === false ? '' : '
					LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)') . '
					LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
					LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
				WHERE t.' . $query_this_board . '
					AND t.id_last_msg >= {int:min_message}
					AND (IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0))) < t.id_last_msg
					AND t.approved = {int:is_approved}
				ORDER BY {raw:order}
				LIMIT {int:offset}, {int:limit}', array_merge($query_parameters, array('current_member' => $user_info['id'], 'min_message' => (int) $min_message, 'is_approved' => 1, 'order' => $_REQUEST['sort'] . ($ascending ? '' : ' DESC'), 'offset' => $_REQUEST['start'], 'limit' => $context['topics_per_page'], 'sort' => $_REQUEST['sort'])));
        }
        $topics = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $topics[] = $row['id_topic'];
        }
        $smcFunc['db_free_result']($request);
        // Sanity... where have you gone?
        if (empty($topics)) {
            $context['topics'] = array();
            if ($context['querystring_board_limits'] == ';start=%d') {
                $context['querystring_board_limits'] = '';
            } else {
                $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']);
            }
            return;
        }
        $request = $smcFunc['db_query']('substring', '
			SELECT ' . $select_clause . '
			FROM {db_prefix}topics AS t
				INNER JOIN {db_prefix}messages AS ms ON (ms.id_topic = t.id_topic AND ms.id_msg = t.id_first_msg)
				INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
				LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)
				LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)
				LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
				LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
			WHERE t.id_topic IN ({array_int:topic_list})
			ORDER BY ' . $_REQUEST['sort'] . ($ascending ? '' : ' DESC') . '
			LIMIT ' . count($topics), array('current_member' => $user_info['id'], 'topic_list' => $topics));
    }
    $context['topics'] = array();
    $topic_ids = array();
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        if ($row['id_poll'] > 0 && $modSettings['pollMode'] == '0') {
            continue;
        }
        $topic_ids[] = $row['id_topic'];
        if (!empty($settings['message_index_preview'])) {
            // Limit them to 128 characters - do this FIRST because it's a lot of wasted censoring otherwise.
            $row['first_body'] = strip_tags(strtr(parse_bbc($row['first_body'], $row['first_smileys'], $row['id_first_msg']), array('<br />' => '&#10;')));
            if ($smcFunc['strlen']($row['first_body']) > 128) {
                $row['first_body'] = $smcFunc['substr']($row['first_body'], 0, 128) . '...';
            }
            $row['last_body'] = strip_tags(strtr(parse_bbc($row['last_body'], $row['last_smileys'], $row['id_last_msg']), array('<br />' => '&#10;')));
            if ($smcFunc['strlen']($row['last_body']) > 128) {
                $row['last_body'] = $smcFunc['substr']($row['last_body'], 0, 128) . '...';
            }
            // Censor the subject and message preview.
            censorText($row['first_subject']);
            censorText($row['first_body']);
            // Don't censor them twice!
            if ($row['id_first_msg'] == $row['id_last_msg']) {
                $row['last_subject'] = $row['first_subject'];
                $row['last_body'] = $row['first_body'];
            } else {
                censorText($row['last_subject']);
                censorText($row['last_body']);
            }
        } else {
            $row['first_body'] = '';
            $row['last_body'] = '';
            censorText($row['first_subject']);
            if ($row['id_first_msg'] == $row['id_last_msg']) {
                $row['last_subject'] = $row['first_subject'];
            } else {
                censorText($row['last_subject']);
            }
        }
        // Decide how many pages the topic should have.
        $topic_length = $row['num_replies'] + 1;
        $messages_per_page = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) && !WIRELESS ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
        if ($topic_length > $messages_per_page) {
            $tmppages = array();
            $tmpa = 1;
            for ($tmpb = 0; $tmpb < $topic_length; $tmpb += $messages_per_page) {
                $tmppages[] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.' . $tmpb . ';topicseen">' . $tmpa . '</a>';
                $tmpa++;
            }
            // Show links to all the pages?
            if (count($tmppages) <= 5) {
                $pages = '&#171; ' . implode(' ', $tmppages);
            } else {
                $pages = '&#171; ' . $tmppages[0] . ' ' . $tmppages[1] . ' ... ' . $tmppages[count($tmppages) - 2] . ' ' . $tmppages[count($tmppages) - 1];
            }
            if (!empty($modSettings['enableAllMessages']) && $topic_length < $modSettings['enableAllMessages']) {
                $pages .= ' &nbsp;<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0;all">' . $txt['all'] . '</a>';
            }
            $pages .= ' &#187;';
        } else {
            $pages = '';
        }
        // We need to check the topic icons exist... you can never be too sure!
        if (empty($modSettings['messageIconChecks_disable'])) {
            // First icon first... as you'd expect.
            if (!isset($context['icon_sources'][$row['first_icon']])) {
                $context['icon_sources'][$row['first_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['first_icon'] . '.gif') ? 'images_url' : 'default_images_url';
            }
            // Last icon... last... duh.
            if (!isset($context['icon_sources'][$row['last_icon']])) {
                $context['icon_sources'][$row['last_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['last_icon'] . '.gif') ? 'images_url' : 'default_images_url';
            }
        }
        // And build the array.
        $context['topics'][$row['id_topic']] = array('id' => $row['id_topic'], 'first_post' => array('id' => $row['id_first_msg'], 'member' => array('name' => $row['first_poster_name'], 'id' => $row['id_first_member'], 'href' => $scripturl . '?action=profile;u=' . $row['id_first_member'], 'link' => !empty($row['id_first_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_first_member'] . '" title="' . $txt['profile_of'] . ' ' . $row['first_poster_name'] . '">' . $row['first_poster_name'] . '</a>' : $row['first_poster_name']), 'time' => timeformat($row['first_poster_time']), 'timestamp' => forum_time(true, $row['first_poster_time']), 'subject' => $row['first_subject'], 'preview' => $row['first_body'], 'icon' => $row['first_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.gif', 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0;topicseen', 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0;topicseen">' . $row['first_subject'] . '</a>'), 'last_post' => array('id' => $row['id_last_msg'], 'member' => array('name' => $row['last_poster_name'], 'id' => $row['id_last_member'], 'href' => $scripturl . '?action=profile;u=' . $row['id_last_member'], 'link' => !empty($row['id_last_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_last_member'] . '">' . $row['last_poster_name'] . '</a>' : $row['last_poster_name']), 'time' => timeformat($row['last_poster_time']), 'timestamp' => forum_time(true, $row['last_poster_time']), 'subject' => $row['last_subject'], 'preview' => $row['last_body'], 'icon' => $row['last_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['last_icon']]] . '/post/' . $row['last_icon'] . '.gif', 'href' => $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['id_last_msg']) . ';topicseen#msg' . $row['id_last_msg'], 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['id_last_msg']) . ';topicseen#msg' . $row['id_last_msg'] . '" rel="nofollow">' . $row['last_subject'] . '</a>'), 'new_from' => $row['new_from'], 'new_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['new_from'] . ';topicseen#new', 'href' => $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['new_from']) . ';topicseen' . ($row['num_replies'] == 0 ? '' : 'new'), 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['new_from']) . ';topicseen#msg' . $row['new_from'] . '" rel="nofollow">' . $row['first_subject'] . '</a>', 'is_sticky' => !empty($modSettings['enableStickyTopics']) && !empty($row['is_sticky']), 'is_locked' => !empty($row['locked']), 'is_poll' => $modSettings['pollMode'] == '1' && $row['id_poll'] > 0, 'is_hot' => $row['num_replies'] >= $modSettings['hotTopicPosts'], 'is_very_hot' => $row['num_replies'] >= $modSettings['hotTopicVeryPosts'], 'is_posted_in' => false, 'icon' => $row['first_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.gif', 'subject' => $row['first_subject'], 'pages' => $pages, 'replies' => comma_format($row['num_replies']), 'views' => comma_format($row['num_views']), 'board' => array('id' => $row['id_board'], 'name' => $row['bname'], 'href' => $scripturl . '?board=' . $row['id_board'] . '.0', 'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['bname'] . '</a>'));
        determineTopicClass($context['topics'][$row['id_topic']]);
    }
    $smcFunc['db_free_result']($request);
    if ($is_topics && !empty($modSettings['enableParticipation']) && !empty($topic_ids)) {
        $result = $smcFunc['db_query']('', '
			SELECT id_topic
			FROM {db_prefix}messages
			WHERE id_topic IN ({array_int:topic_list})
				AND id_member = {int:current_member}
			GROUP BY id_topic
			LIMIT {int:limit}', array('current_member' => $user_info['id'], 'topic_list' => $topic_ids, 'limit' => count($topic_ids)));
        while ($row = $smcFunc['db_fetch_assoc']($result)) {
            if (empty($context['topics'][$row['id_topic']]['is_posted_in'])) {
                $context['topics'][$row['id_topic']]['is_posted_in'] = true;
                $context['topics'][$row['id_topic']]['class'] = 'my_' . $context['topics'][$row['id_topic']]['class'];
            }
        }
        $smcFunc['db_free_result']($result);
    }
    $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']);
    $context['topics_to_mark'] = implode('-', $topic_ids);
}
    /**
     * The central part of the board - topic display.
     *
     * What it does:
     * - This function loads the posts in a topic up so they can be displayed.
     * - It uses the main sub template of the Display template.
     * - It requires a topic, and can go to the previous or next topic from it.
     * - It jumps to the correct post depending on a number/time/IS_MSG passed.
     * - It depends on the messages_per_page, defaultMaxMessages and enableAllMessages settings.
     * - It is accessed by ?topic=id_topic.START.
     */
    public function action_display()
    {
        global $scripturl, $txt, $modSettings, $context, $settings;
        global $options, $user_info, $board_info, $topic, $board;
        global $attachments, $messages_request;
        // What are you gonna display if these are empty?!
        if (empty($topic)) {
            fatal_lang_error('no_board', false);
        }
        // Load the template
        loadTemplate('Display');
        $context['sub_template'] = 'messages';
        // And the topic functions
        require_once SUBSDIR . '/Topic.subs.php';
        require_once SUBSDIR . '/Messages.subs.php';
        // Not only does a prefetch make things slower for the server, but it makes it impossible to know if they read it.
        if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') {
            @ob_end_clean();
            header('HTTP/1.1 403 Prefetch Forbidden');
            die;
        }
        // How much are we sticking on each page?
        $context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
        $template_layers = Template_Layers::getInstance();
        $template_layers->addEnd('messages_informations');
        $includeUnapproved = !$modSettings['postmod_active'] || allowedTo('approve_posts');
        // Let's do some work on what to search index.
        if (count($_GET) > 2) {
            foreach ($_GET as $k => $v) {
                if (!in_array($k, array('topic', 'board', 'start', session_name()))) {
                    $context['robot_no_index'] = true;
                }
            }
        }
        if (!empty($_REQUEST['start']) && (!is_numeric($_REQUEST['start']) || $_REQUEST['start'] % $context['messages_per_page'] != 0)) {
            $context['robot_no_index'] = true;
        }
        // Find the previous or next topic.  Make a fuss if there are no more.
        if (isset($_REQUEST['prev_next']) && ($_REQUEST['prev_next'] == 'prev' || $_REQUEST['prev_next'] == 'next')) {
            // No use in calculating the next topic if there's only one.
            if ($board_info['num_topics'] > 1) {
                $includeStickies = !empty($modSettings['enableStickyTopics']);
                $topic = $_REQUEST['prev_next'] === 'prev' ? previousTopic($topic, $board, $user_info['id'], $includeUnapproved, $includeStickies) : nextTopic($topic, $board, $user_info['id'], $includeUnapproved, $includeStickies);
                $context['current_topic'] = $topic;
            }
            // Go to the newest message on this topic.
            $_REQUEST['start'] = 'new';
        }
        // Add 1 to the number of views of this topic (except for robots).
        if (!$user_info['possibly_robot'] && (empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic)) {
            increaseViewCounter($topic);
            $_SESSION['last_read_topic'] = $topic;
        }
        $topic_selects = array();
        $topic_tables = array();
        $topic_parameters = array('topic' => $topic, 'member' => $user_info['id'], 'board' => (int) $board);
        // Allow addons to add additional details to the topic query
        call_integration_hook('integrate_topic_query', array(&$topic_selects, &$topic_tables, &$topic_parameters));
        // Load the topic details
        $topicinfo = getTopicInfo($topic_parameters, 'all', $topic_selects, $topic_tables);
        if (empty($topicinfo)) {
            fatal_lang_error('not_a_topic', false);
        }
        // Is this a moved topic that we are redirecting to?
        if (!empty($topicinfo['id_redirect_topic']) && !isset($_GET['noredir'])) {
            markTopicsRead(array($user_info['id'], $topic, $topicinfo['id_last_msg'], 0), $topicinfo['new_from'] !== 0);
            redirectexit('topic=' . $topicinfo['id_redirect_topic'] . '.0;redirfrom=' . $topicinfo['id_topic']);
        }
        $context['real_num_replies'] = $context['num_replies'] = $topicinfo['num_replies'];
        $context['topic_first_message'] = $topicinfo['id_first_msg'];
        $context['topic_last_message'] = $topicinfo['id_last_msg'];
        $context['topic_unwatched'] = isset($topicinfo['unwatched']) ? $topicinfo['unwatched'] : 0;
        if (isset($_GET['redirfrom'])) {
            $redir_topics = topicsList(array((int) $_GET['redirfrom']));
            if (!empty($redir_topics[(int) $_GET['redirfrom']])) {
                $context['topic_redirected_from'] = $redir_topics[(int) $_GET['redirfrom']];
                $context['topic_redirected_from']['redir_href'] = $scripturl . '?topic=' . $context['topic_redirected_from']['id_topic'] . '.0;noredir';
            }
        }
        // Add up unapproved replies to get real number of replies...
        if ($modSettings['postmod_active'] && allowedTo('approve_posts')) {
            $context['real_num_replies'] += $topicinfo['unapproved_posts'] - ($topicinfo['approved'] ? 0 : 1);
        }
        // If this topic was derived from another, set the followup details
        if (!empty($topicinfo['derived_from'])) {
            require_once SUBSDIR . '/FollowUps.subs.php';
            $context['topic_derived_from'] = topicStartedHere($topic, $includeUnapproved);
        }
        // If this topic has unapproved posts, we need to work out how many posts the user can see, for page indexing.
        if (!$includeUnapproved && $topicinfo['unapproved_posts'] && !$user_info['is_guest']) {
            $myUnapprovedPosts = unapprovedPosts($topic, $user_info['id']);
            $context['total_visible_posts'] = $context['num_replies'] + $myUnapprovedPosts + ($topicinfo['approved'] ? 1 : 0);
        } elseif ($user_info['is_guest']) {
            $context['total_visible_posts'] = $context['num_replies'] + ($topicinfo['approved'] ? 1 : 0);
        } else {
            $context['total_visible_posts'] = $context['num_replies'] + $topicinfo['unapproved_posts'] + ($topicinfo['approved'] ? 1 : 0);
        }
        // When was the last time this topic was replied to?  Should we warn them about it?
        if (!empty($modSettings['oldTopicDays'])) {
            $mgsOptions = basicMessageInfo($topicinfo['id_last_msg'], true);
            $context['oldTopicError'] = $mgsOptions['poster_time'] + $modSettings['oldTopicDays'] * 86400 < time() && empty($topicinfo['is_sticky']);
        } else {
            $context['oldTopicError'] = false;
        }
        // The start isn't a number; it's information about what to do, where to go.
        if (!is_numeric($_REQUEST['start'])) {
            // Redirect to the page and post with new messages, originally by Omar Bazavilvazo.
            if ($_REQUEST['start'] == 'new') {
                // Guests automatically go to the last post.
                if ($user_info['is_guest']) {
                    $context['start_from'] = $context['total_visible_posts'] - 1;
                    $_REQUEST['start'] = $context['start_from'];
                } else {
                    // Fall through to the next if statement.
                    $_REQUEST['start'] = 'msg' . $topicinfo['new_from'];
                }
            }
            // Start from a certain time index, not a message.
            if (substr($_REQUEST['start'], 0, 4) == 'from') {
                $timestamp = (int) substr($_REQUEST['start'], 4);
                if ($timestamp === 0) {
                    $_REQUEST['start'] = 0;
                } else {
                    // Find the number of messages posted before said time...
                    $context['start_from'] = countNewPosts($topic, $topicinfo, $timestamp);
                    $_REQUEST['start'] = $context['start_from'];
                }
            } elseif (substr($_REQUEST['start'], 0, 3) == 'msg') {
                $virtual_msg = (int) substr($_REQUEST['start'], 3);
                if (!$topicinfo['unapproved_posts'] && $virtual_msg >= $topicinfo['id_last_msg']) {
                    $context['start_from'] = $context['total_visible_posts'] - 1;
                } elseif (!$topicinfo['unapproved_posts'] && $virtual_msg <= $topicinfo['id_first_msg']) {
                    $context['start_from'] = 0;
                } else {
                    $only_approved = $modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts');
                    $context['start_from'] = countMessagesBefore($topic, $virtual_msg, false, $only_approved, !$user_info['is_guest']);
                }
                // We need to reverse the start as well in this case.
                $_REQUEST['start'] = $context['start_from'];
            }
        }
        // Mark the mention as read if requested
        if (isset($_REQUEST['mentionread']) && !empty($virtual_msg)) {
            require_once CONTROLLERDIR . '/Mentions.controller.php';
            $mentions = new Mentions_Controller();
            $mentions->setData(array('id_mention' => $_REQUEST['item'], 'mark' => $_REQUEST['mark']));
            $mentions->action_markread();
        }
        // Create a previous next string if the selected theme has it as a selected option.
        if ($modSettings['enablePreviousNext']) {
            $context['links'] += array('go_prev' => $scripturl . '?topic=' . $topic . '.0;prev_next=prev#new', 'go_next' => $scripturl . '?topic=' . $topic . '.0;prev_next=next#new');
        }
        // Derived from, set the link back
        if (!empty($context['topic_derived_from'])) {
            $context['links']['derived_from'] = $scripturl . '?msg=' . $context['topic_derived_from']['derived_from'];
        }
        // Check if spellchecking is both enabled and actually working. (for quick reply.)
        $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
        if ($context['show_spellchecking']) {
            loadJavascriptFile('spellcheck.js', array('defer' => true));
        }
        // Do we need to show the visual verification image?
        $context['require_verification'] = !$user_info['is_mod'] && !$user_info['is_admin'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || $user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1);
        if ($context['require_verification']) {
            require_once SUBSDIR . '/VerificationControls.class.php';
            $verificationOptions = array('id' => 'post');
            $context['require_verification'] = create_control_verification($verificationOptions);
            $context['visual_verification_id'] = $verificationOptions['id'];
        }
        // Are we showing signatures - or disabled fields?
        $context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1;
        $context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
        // Censor the title...
        censorText($topicinfo['subject']);
        $context['page_title'] = $topicinfo['subject'];
        // Is this topic sticky, or can it even be?
        $topicinfo['is_sticky'] = empty($modSettings['enableStickyTopics']) ? '0' : $topicinfo['is_sticky'];
        // Allow addons access to the topicinfo array
        call_integration_hook('integrate_display_topic', array($topicinfo));
        // Default this topic to not marked for notifications... of course...
        $context['is_marked_notify'] = false;
        // Did we report a post to a moderator just now?
        $context['report_sent'] = isset($_GET['reportsent']);
        if ($context['report_sent']) {
            $template_layers->add('report_sent');
        }
        // Let's get nosey, who is viewing this topic?
        if (!empty($settings['display_who_viewing'])) {
            require_once SUBSDIR . '/Who.subs.php';
            formatViewers($topic, 'topic');
        }
        // If all is set, but not allowed... just unset it.
        $can_show_all = !empty($modSettings['enableAllMessages']) && $context['total_visible_posts'] > $context['messages_per_page'] && $context['total_visible_posts'] < $modSettings['enableAllMessages'];
        if (isset($_REQUEST['all']) && !$can_show_all) {
            unset($_REQUEST['all']);
        } elseif (isset($_REQUEST['all'])) {
            $_REQUEST['start'] = -1;
        }
        // Construct the page index, allowing for the .START method...
        $context['page_index'] = constructPageIndex($scripturl . '?topic=' . $topic . '.%1$d', $_REQUEST['start'], $context['total_visible_posts'], $context['messages_per_page'], true, array('all' => $can_show_all, 'all_selected' => isset($_REQUEST['all'])));
        $context['start'] = $_REQUEST['start'];
        // This is information about which page is current, and which page we're on - in case you don't like the constructed page index. (again, wireles..)
        $context['page_info'] = array('current_page' => $_REQUEST['start'] / $context['messages_per_page'] + 1, 'num_pages' => floor(($context['total_visible_posts'] - 1) / $context['messages_per_page']) + 1);
        // Figure out all the link to the next/prev
        $context['links'] += array('prev' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] - $context['messages_per_page']) : '', 'next' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] + $context['messages_per_page']) : '');
        // If they are viewing all the posts, show all the posts, otherwise limit the number.
        if ($can_show_all && isset($_REQUEST['all'])) {
            // No limit! (actually, there is a limit, but...)
            $context['messages_per_page'] = -1;
            // Set start back to 0...
            $_REQUEST['start'] = 0;
        }
        // Build the link tree.
        $context['linktree'][] = array('url' => $scripturl . '?topic=' . $topic . '.0', 'name' => $topicinfo['subject']);
        // Build a list of this board's moderators.
        $context['moderators'] =& $board_info['moderators'];
        $context['link_moderators'] = array();
        // Information about the current topic...
        $context['is_locked'] = $topicinfo['locked'];
        $context['is_sticky'] = $topicinfo['is_sticky'];
        $context['is_very_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicVeryPosts'];
        $context['is_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicPosts'];
        $context['is_approved'] = $topicinfo['approved'];
        $context['is_poll'] = $topicinfo['id_poll'] > 0 && !empty($modSettings['pollMode']) && allowedTo('poll_view');
        determineTopicClass($context);
        // Did this user start the topic or not?
        $context['user']['started'] = $user_info['id'] == $topicinfo['id_member_started'] && !$user_info['is_guest'];
        $context['topic_starter_id'] = $topicinfo['id_member_started'];
        // Set the topic's information for the template.
        $context['subject'] = $topicinfo['subject'];
        $context['num_views'] = $topicinfo['num_views'];
        $context['num_views_text'] = $context['num_views'] == 1 ? $txt['read_one_time'] : sprintf($txt['read_many_times'], $context['num_views']);
        $context['mark_unread_time'] = !empty($virtual_msg) ? $virtual_msg : $topicinfo['new_from'];
        // Set a canonical URL for this page.
        $context['canonical_url'] = $scripturl . '?topic=' . $topic . '.' . $context['start'];
        // For quick reply we need a response prefix in the default forum language.
        $context['response_prefix'] = response_prefix();
        // If we want to show event information in the topic, prepare the data.
        if (allowedTo('calendar_view') && !empty($modSettings['cal_showInTopic']) && !empty($modSettings['cal_enabled'])) {
            // We need events details and all that jazz
            require_once SUBSDIR . '/Calendar.subs.php';
            // First, try create a better time format, ignoring the "time" elements.
            if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0])) {
                $date_string = $user_info['time_format'];
            } else {
                $date_string = $matches[0];
            }
            // Get event information for this topic.
            $events = eventInfoForTopic($topic);
            $context['linked_calendar_events'] = array();
            foreach ($events as $event) {
                // Prepare the dates for being formatted.
                $start_date = sscanf($event['start_date'], '%04d-%02d-%02d');
                $start_date = mktime(12, 0, 0, $start_date[1], $start_date[2], $start_date[0]);
                $end_date = sscanf($event['end_date'], '%04d-%02d-%02d');
                $end_date = mktime(12, 0, 0, $end_date[1], $end_date[2], $end_date[0]);
                $context['linked_calendar_events'][] = array('id' => $event['id_event'], 'title' => $event['title'], 'can_edit' => allowedTo('calendar_edit_any') || $event['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own'), 'modify_href' => $scripturl . '?action=post;msg=' . $topicinfo['id_first_msg'] . ';topic=' . $topic . '.0;calendar;eventid=' . $event['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'can_export' => allowedTo('calendar_edit_any') || $event['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own'), 'export_href' => $scripturl . '?action=calendar;sa=ical;eventid=' . $event['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'start_date' => standardTime($start_date, $date_string, 'none'), 'start_timestamp' => $start_date, 'end_date' => standardTime($end_date, $date_string, 'none'), 'end_timestamp' => $end_date, 'is_last' => false);
            }
            if (!empty($context['linked_calendar_events'])) {
                $context['linked_calendar_events'][count($context['linked_calendar_events']) - 1]['is_last'] = true;
                $template_layers->add('display_calendar');
            }
        }
        // Create the poll info if it exists.
        if ($context['is_poll']) {
            $template_layers->add('display_poll');
            require_once SUBSDIR . '/Poll.subs.php';
            loadPollContext($topicinfo['id_poll']);
            // Build the poll moderation button array.
            $context['poll_buttons'] = array('vote' => array('test' => 'allow_return_vote', 'text' => 'poll_return_vote', 'image' => 'poll_options.png', 'lang' => true, 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start']), 'results' => array('test' => 'allow_poll_view', 'text' => 'poll_results', 'image' => 'poll_results.png', 'lang' => true, 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start'] . ';viewresults'), 'change_vote' => array('test' => 'allow_change_vote', 'text' => 'poll_change_vote', 'image' => 'poll_change_vote.png', 'lang' => true, 'url' => $scripturl . '?action=poll;sa=vote;topic=' . $context['current_topic'] . '.' . $context['start'] . ';poll=' . $context['poll']['id'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'lock' => array('test' => 'allow_lock_poll', 'text' => !$context['poll']['is_locked'] ? 'poll_lock' : 'poll_unlock', 'image' => 'poll_lock.png', 'lang' => true, 'url' => $scripturl . '?action=lockvoting;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'edit' => array('test' => 'allow_edit_poll', 'text' => 'poll_edit', 'image' => 'poll_edit.png', 'lang' => true, 'url' => $scripturl . '?action=editpoll;topic=' . $context['current_topic'] . '.' . $context['start']), 'remove_poll' => array('test' => 'can_remove_poll', 'text' => 'poll_remove', 'image' => 'admin_remove_poll.png', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . $txt['poll_remove_warn'] . '\');"', 'url' => $scripturl . '?action=poll;sa=remove;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']));
            // Allow mods to add additional buttons here
            call_integration_hook('integrate_poll_buttons');
        }
        // Calculate the fastest way to get the messages!
        $ascending = true;
        $start = $_REQUEST['start'];
        $limit = $context['messages_per_page'];
        $firstIndex = 0;
        if ($start >= $context['total_visible_posts'] / 2 && $context['messages_per_page'] != -1) {
            $ascending = !$ascending;
            $limit = $context['total_visible_posts'] <= $start + $limit ? $context['total_visible_posts'] - $start : $limit;
            $start = $context['total_visible_posts'] <= $start + $limit ? 0 : $context['total_visible_posts'] - $start - $limit;
            $firstIndex = $limit - 1;
        }
        // Taking care of member specific settings
        $limit_settings = array('messages_per_page' => $context['messages_per_page'], 'start' => $start, 'offset' => $limit);
        // Get each post and poster in this topic.
        $topic_details = getTopicsPostsAndPoster($topic, $limit_settings, $ascending);
        $messages = $topic_details['messages'];
        $posters = array_unique($topic_details['all_posters']);
        $all_posters = $topic_details['all_posters'];
        unset($topic_details);
        call_integration_hook('integrate_display_message_list', array(&$messages, &$posters));
        // Guests can't mark topics read or for notifications, just can't sorry.
        if (!$user_info['is_guest'] && !empty($messages)) {
            $mark_at_msg = max($messages);
            if ($mark_at_msg >= $topicinfo['id_last_msg']) {
                $mark_at_msg = $modSettings['maxMsgID'];
            }
            if ($mark_at_msg >= $topicinfo['new_from']) {
                markTopicsRead(array($user_info['id'], $topic, $mark_at_msg, $topicinfo['unwatched']), $topicinfo['new_from'] !== 0);
            }
            updateReadNotificationsFor($topic, $board);
            // Have we recently cached the number of new topics in this board, and it's still a lot?
            if (isset($_REQUEST['topicseen']) && isset($_SESSION['topicseen_cache'][$board]) && $_SESSION['topicseen_cache'][$board] > 5) {
                $_SESSION['topicseen_cache'][$board]--;
            } elseif (isset($_REQUEST['topicseen'])) {
                // Use the mark read tables... and the last visit to figure out if this should be read or not.
                $numNewTopics = getUnreadCountSince($board, empty($_SESSION['id_msg_last_visit']) ? 0 : $_SESSION['id_msg_last_visit']);
                // If there're no real new topics in this board, mark the board as seen.
                if (empty($numNewTopics)) {
                    $_REQUEST['boardseen'] = true;
                } else {
                    $_SESSION['topicseen_cache'][$board] = $numNewTopics;
                }
            } elseif (isset($_SESSION['topicseen_cache'][$board])) {
                $_SESSION['topicseen_cache'][$board]--;
            }
            // Mark board as seen if we came using last post link from BoardIndex. (or other places...)
            if (isset($_REQUEST['boardseen'])) {
                require_once SUBSDIR . '/Boards.subs.php';
                markBoardsRead($board, false, false);
            }
        }
        $attachments = array();
        // If there _are_ messages here... (probably an error otherwise :!)
        if (!empty($messages)) {
            require_once SUBSDIR . '/Attachments.subs.php';
            // Fetch attachments.
            $includeUnapproved = !$modSettings['postmod_active'] || allowedTo('approve_posts');
            if (!empty($modSettings['attachmentEnable']) && allowedTo('view_attachments')) {
                $attachments = getAttachments($messages, $includeUnapproved, 'filter_accessible_attachment', $all_posters);
            }
            $msg_parameters = array('message_list' => $messages, 'new_from' => $topicinfo['new_from']);
            $msg_selects = array();
            $msg_tables = array();
            call_integration_hook('integrate_message_query', array(&$msg_selects, &$msg_tables, &$msg_parameters));
            // What?  It's not like it *couldn't* be only guests in this topic...
            if (!empty($posters)) {
                loadMemberData($posters);
            }
            // Load in the likes for this group of messages
            if (!empty($modSettings['likes_enabled'])) {
                require_once SUBSDIR . '/Likes.subs.php';
                $context['likes'] = loadLikes($messages, true);
                // ajax controller for likes
                loadJavascriptFile('like_posts.js', array('defer' => true));
                loadLanguage('Errors');
                // Initiate likes and the tooltips for likes
                addInlineJavascript('
				$(document).ready(function () {
					var likePostInstance = likePosts.prototype.init({
						oTxt: ({
							btnText : ' . JavaScriptEscape($txt['ok_uppercase']) . ',
							likeHeadingError : ' . JavaScriptEscape($txt['like_heading_error']) . ',
							error_occurred : ' . JavaScriptEscape($txt['error_occurred']) . '
						}),
					});

					$(".like_button, .unlike_button").SiteTooltip({
						hoverIntent: {
							sensitivity: 10,
							interval: 150,
							timeout: 50
						}
					});
				});', true);
            }
            $messages_request = loadMessageRequest($msg_selects, $msg_tables, $msg_parameters);
            if (!empty($modSettings['enableFollowup'])) {
                require_once SUBSDIR . '/FollowUps.subs.php';
                $context['follow_ups'] = followupTopics($messages, $includeUnapproved);
            }
            // Go to the last message if the given time is beyond the time of the last message.
            if (isset($context['start_from']) && $context['start_from'] >= $topicinfo['num_replies']) {
                $context['start_from'] = $topicinfo['num_replies'];
            }
            // Since the anchor information is needed on the top of the page we load these variables beforehand.
            $context['first_message'] = isset($messages[$firstIndex]) ? $messages[$firstIndex] : $messages[0];
            $context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $context['start_from'];
        } else {
            $messages_request = false;
            $context['first_message'] = 0;
            $context['first_new_message'] = false;
        }
        $context['jump_to'] = array('label' => addslashes(un_htmlspecialchars($txt['jump_to'])), 'board_name' => htmlspecialchars(strtr(strip_tags($board_info['name']), array('&amp;' => '&')), ENT_COMPAT, 'UTF-8'), 'child_level' => $board_info['child_level']);
        // Set the callback.  (do you REALIZE how much memory all the messages would take?!?)
        // This will be called from the template.
        $context['get_message'] = array($this, 'prepareDisplayContext_callback');
        // Now set all the wonderful, wonderful permissions... like moderation ones...
        $common_permissions = array('can_approve' => 'approve_posts', 'can_ban' => 'manage_bans', 'can_sticky' => 'make_sticky', 'can_merge' => 'merge_any', 'can_split' => 'split_any', 'calendar_post' => 'calendar_post', 'can_mark_notify' => 'mark_any_notify', 'can_send_topic' => 'send_topic', 'can_send_pm' => 'pm_send', 'can_send_email' => 'send_email_to_members', 'can_report_moderator' => 'report_any', 'can_moderate_forum' => 'moderate_forum', 'can_issue_warning' => 'issue_warning', 'can_restore_topic' => 'move_any', 'can_restore_msg' => 'move_any');
        foreach ($common_permissions as $contextual => $perm) {
            $context[$contextual] = allowedTo($perm);
        }
        // Permissions with _any/_own versions.  $context[YYY] => ZZZ_any/_own.
        $anyown_permissions = array('can_move' => 'move', 'can_lock' => 'lock', 'can_delete' => 'remove', 'can_add_poll' => 'poll_add', 'can_remove_poll' => 'poll_remove', 'can_reply' => 'post_reply', 'can_reply_unapproved' => 'post_unapproved_replies');
        foreach ($anyown_permissions as $contextual => $perm) {
            $context[$contextual] = allowedTo($perm . '_any') || $context['user']['started'] && allowedTo($perm . '_own');
        }
        // Cleanup all the permissions with extra stuff...
        $context['can_mark_notify'] &= !$context['user']['is_guest'];
        $context['can_sticky'] &= !empty($modSettings['enableStickyTopics']);
        $context['calendar_post'] &= !empty($modSettings['cal_enabled']) && (allowedTo('modify_any') || $context['user']['started'] && allowedTo('modify_own'));
        $context['can_add_poll'] &= !empty($modSettings['pollMode']) && $topicinfo['id_poll'] <= 0;
        $context['can_remove_poll'] &= !empty($modSettings['pollMode']) && $topicinfo['id_poll'] > 0;
        $context['can_reply'] &= empty($topicinfo['locked']) || allowedTo('moderate_board');
        $context['can_reply_unapproved'] &= $modSettings['postmod_active'] && (empty($topicinfo['locked']) || allowedTo('moderate_board'));
        $context['can_issue_warning'] &= in_array('w', $context['admin_features']) && !empty($modSettings['warning_enable']);
        // Handle approval flags...
        $context['can_reply_approved'] = $context['can_reply'];
        $context['can_reply'] |= $context['can_reply_unapproved'];
        $context['can_quote'] = $context['can_reply'] && (empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])));
        $context['can_mark_unread'] = !$user_info['is_guest'] && $settings['show_mark_read'];
        $context['can_unwatch'] = !$user_info['is_guest'] && $modSettings['enable_unwatch'];
        $context['can_send_topic'] = (!$modSettings['postmod_active'] || $topicinfo['approved']) && allowedTo('send_topic');
        $context['can_print'] = empty($modSettings['disable_print_topic']);
        // Start this off for quick moderation - it will be or'd for each post.
        $context['can_remove_post'] = allowedTo('delete_any') || allowedTo('delete_replies') && $context['user']['started'];
        // Can restore topic?  That's if the topic is in the recycle board and has a previous restore state.
        $context['can_restore_topic'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_board']);
        $context['can_restore_msg'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_topic']);
        $context['can_follow_up'] = !empty($modSettings['enableFollowup']) && boardsallowedto('post_new') !== array();
        // Check if the draft functions are enabled and that they have permission to use them (for quick reply.)
        $context['drafts_save'] = !empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft') && $context['can_reply'];
        $context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('post_autosave_draft');
        if (!empty($context['drafts_save'])) {
            loadLanguage('Drafts');
        }
        if (!empty($context['drafts_autosave']) && empty($options['use_editor_quick_reply'])) {
            loadJavascriptFile('drafts.js');
        }
        if (!empty($modSettings['mentions_enabled'])) {
            $context['mentions_enabled'] = true;
            // Just using the plain text quick reply and not the editor
            if (empty($options['use_editor_quick_reply'])) {
                loadJavascriptFile(array('jquery.atwho.js', 'jquery.caret.min.js', 'mentioning.js'));
            }
            loadCSSFile('jquery.atwho.css');
            addInlineJavascript('
			$(document).ready(function () {
				for (var i = 0, count = all_elk_mentions.length; i < count; i++)
					all_elk_mentions[i].oMention = new elk_mentions(all_elk_mentions[i].oOptions);
			});');
        }
        // Load up the Quick ModifyTopic and Quick Reply scripts
        loadJavascriptFile('topic.js');
        // Auto video embeding enabled?
        if (!empty($modSettings['enableVideoEmbeding'])) {
            addInlineJavascript('
		$(document).ready(function() {
			$().linkifyvideo(oEmbedtext);
		});');
        }
        // Load up the "double post" sequencing magic.
        if (!empty($options['display_quick_reply'])) {
            checkSubmitOnce('register');
            $context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
            $context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
            if (!empty($options['use_editor_quick_reply']) && $context['can_reply']) {
                // Needed for the editor and message icons.
                require_once SUBSDIR . '/Editor.subs.php';
                // Now create the editor.
                $editorOptions = array('id' => 'message', 'value' => '', 'labels' => array('post_button' => $txt['post']), 'height' => '250px', 'width' => '100%', 'preview_type' => 0);
                create_control_richedit($editorOptions);
                $context['attached'] = '';
                $context['make_poll'] = isset($_REQUEST['poll']);
                // Message icons - customized icons are off?
                $context['icons'] = getMessageIcons($board);
                if (!empty($context['icons'])) {
                    $context['icons'][count($context['icons']) - 1]['is_last'] = true;
                }
            }
        }
        addJavascriptVar(array('notification_topic_notice' => $context['is_marked_notify'] ? $txt['notification_disable_topic'] : $txt['notification_enable_topic']), true);
        if ($context['can_send_topic']) {
            addJavascriptVar(array('sendtopic_cancel' => $txt['modify_cancel'], 'sendtopic_back' => $txt['back'], 'sendtopic_close' => $txt['find_close'], 'sendtopic_error' => $txt['send_error_occurred'], 'required_field' => $txt['require_field']), true);
        }
        // Build the normal button array.
        $context['normal_buttons'] = array('reply' => array('test' => 'can_reply', 'text' => 'reply', 'image' => 'reply.png', 'lang' => true, 'url' => $scripturl . '?action=post;topic=' . $context['current_topic'] . '.' . $context['start'] . ';last_msg=' . $context['topic_last_message'], 'active' => true), 'notify' => array('test' => 'can_mark_notify', 'text' => $context['is_marked_notify'] ? 'unnotify' : 'notify', 'image' => ($context['is_marked_notify'] ? 'un' : '') . 'notify.png', 'lang' => true, 'custom' => 'onclick="return notifyButton(this);"', 'url' => $scripturl . '?action=notify;sa=' . ($context['is_marked_notify'] ? 'off' : 'on') . ';topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'mark_unread' => array('test' => 'can_mark_unread', 'text' => 'mark_unread', 'image' => 'markunread.png', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=topic;t=' . $context['mark_unread_time'] . ';topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'unwatch' => array('test' => 'can_unwatch', 'text' => ($context['topic_unwatched'] ? '' : 'un') . 'watch', 'image' => ($context['topic_unwatched'] ? '' : 'un') . 'watched.png', 'lang' => true, 'custom' => 'onclick="return unwatchButton(this);"', 'url' => $scripturl . '?action=unwatchtopic;topic=' . $context['current_topic'] . '.' . $context['start'] . ';sa=' . ($context['topic_unwatched'] ? 'off' : 'on') . ';' . $context['session_var'] . '=' . $context['session_id']), 'send' => array('test' => 'can_send_topic', 'text' => 'send_topic', 'image' => 'sendtopic.png', 'lang' => true, 'url' => $scripturl . '?action=emailuser;sa=sendtopic;topic=' . $context['current_topic'] . '.0', 'custom' => 'onclick="return sendtopicOverlayDiv(this.href, \'' . $txt['send_topic'] . '\');"'), 'print' => array('test' => 'can_print', 'text' => 'print', 'image' => 'print.png', 'lang' => true, 'custom' => 'rel="nofollow"', 'class' => 'new_win', 'url' => $scripturl . '?action=topic;sa=printpage;topic=' . $context['current_topic'] . '.0'));
        // Build the mod button array
        $context['mod_buttons'] = array('move' => array('test' => 'can_move', 'text' => 'move_topic', 'image' => 'admin_move.png', 'lang' => true, 'url' => $scripturl . '?action=movetopic;current_board=' . $context['current_board'] . ';topic=' . $context['current_topic'] . '.0'), 'delete' => array('test' => 'can_delete', 'text' => 'remove_topic', 'image' => 'admin_rem.png', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . $txt['are_sure_remove_topic'] . '\');"', 'url' => $scripturl . '?action=removetopic2;topic=' . $context['current_topic'] . '.0;' . $context['session_var'] . '=' . $context['session_id']), 'lock' => array('test' => 'can_lock', 'text' => empty($context['is_locked']) ? 'set_lock' : 'set_unlock', 'image' => 'admin_lock.png', 'lang' => true, 'url' => $scripturl . '?action=topic;sa=lock;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'sticky' => array('test' => 'can_sticky', 'text' => empty($context['is_sticky']) ? 'set_sticky' : 'set_nonsticky', 'image' => 'admin_sticky.png', 'lang' => true, 'url' => $scripturl . '?action=topic;sa=sticky;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'merge' => array('test' => 'can_merge', 'text' => 'merge', 'image' => 'merge.png', 'lang' => true, 'url' => $scripturl . '?action=mergetopics;board=' . $context['current_board'] . '.0;from=' . $context['current_topic']), 'calendar' => array('test' => 'calendar_post', 'text' => 'calendar_link', 'image' => 'linktocal.png', 'lang' => true, 'url' => $scripturl . '?action=post;calendar;msg=' . $context['topic_first_message'] . ';topic=' . $context['current_topic'] . '.0'));
        // Restore topic. eh?  No monkey business.
        if ($context['can_restore_topic']) {
            $context['mod_buttons'][] = array('text' => 'restore_topic', 'image' => '', 'lang' => true, 'url' => $scripturl . '?action=restoretopic;topics=' . $context['current_topic'] . ';' . $context['session_var'] . '=' . $context['session_id']);
        }
        if ($context['can_reply'] && !empty($options['display_quick_reply'])) {
            $template_layers->add('quickreply');
        }
        $template_layers->add('pages_and_buttons');
        // Allow adding new buttons easily.
        call_integration_hook('integrate_display_buttons');
        call_integration_hook('integrate_mod_buttons');
    }
Beispiel #3
0
function MarkRead()
{
    global $board, $topic, $user_info, $board_info, $modSettings, $smcFunc;
    // No Guests allowed!
    is_not_guest();
    checkSession('get');
    if (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'all') {
        // Find all the boards this user can see.
        $result = $smcFunc['db_query']('', '
			SELECT b.id_board
			FROM {db_prefix}boards AS b
			WHERE {query_see_board}', array());
        $boards = array();
        while ($row = $smcFunc['db_fetch_assoc']($result)) {
            $boards[] = $row['id_board'];
        }
        $smcFunc['db_free_result']($result);
        if (!empty($boards)) {
            markBoardsRead($boards, isset($_REQUEST['unread']));
        }
        $_SESSION['id_msg_last_visit'] = $modSettings['maxMsgID'];
        if (!empty($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'action=unread') !== false) {
            redirectexit('action=unread');
        }
        if (isset($_SESSION['topicseen_cache'])) {
            $_SESSION['topicseen_cache'] = array();
        }
        redirectexit();
    } elseif (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'unreadreplies') {
        // Make sure all the boards are integers!
        $topics = explode('-', $_REQUEST['topics']);
        $markRead = array();
        foreach ($topics as $id_topic) {
            $markRead[] = array($modSettings['maxMsgID'], $user_info['id'], (int) $id_topic);
        }
        $smcFunc['db_insert']('replace', '{db_prefix}log_topics', array('id_msg' => 'int', 'id_member' => 'int', 'id_topic' => 'int'), $markRead, array('id_member', 'id_topic'));
        if (isset($_SESSION['topicseen_cache'])) {
            $_SESSION['topicseen_cache'] = array();
        }
        redirectexit('action=unreadreplies');
    } elseif (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'topic') {
        // First, let's figure out what the latest message is.
        $result = $smcFunc['db_query']('', '
			SELECT id_first_msg, id_last_msg
			FROM {db_prefix}topics
			WHERE id_topic = {int:current_topic}', array('current_topic' => $topic));
        $topicinfo = $smcFunc['db_fetch_assoc']($result);
        $smcFunc['db_free_result']($result);
        if (!empty($_GET['t'])) {
            // If they read the whole topic, go back to the beginning.
            if ($_GET['t'] >= $topicinfo['id_last_msg']) {
                $earlyMsg = 0;
            } elseif ($_GET['t'] <= $topicinfo['id_first_msg']) {
                $earlyMsg = 0;
            } else {
                $result = $smcFunc['db_query']('', '
					SELECT MAX(id_msg)
					FROM {db_prefix}messages
					WHERE id_topic = {int:current_topic}
						AND id_msg >= {int:id_first_msg}
						AND id_msg < {int:topic_msg_id}', array('current_topic' => $topic, 'topic_msg_id' => (int) $_GET['t'], 'id_first_msg' => $topicinfo['id_first_msg']));
                list($earlyMsg) = $smcFunc['db_fetch_row']($result);
                $smcFunc['db_free_result']($result);
            }
        } elseif ($_REQUEST['start'] == 0) {
            $earlyMsg = 0;
        } else {
            $result = $smcFunc['db_query']('', '
				SELECT id_msg
				FROM {db_prefix}messages
				WHERE id_topic = {int:current_topic}
				ORDER BY id_msg
				LIMIT ' . (int) $_REQUEST['start'] . ', 1', array('current_topic' => $topic));
            list($earlyMsg) = $smcFunc['db_fetch_row']($result);
            $smcFunc['db_free_result']($result);
            $earlyMsg--;
        }
        // Blam, unread!
        $smcFunc['db_insert']('replace', '{db_prefix}log_topics', array('id_msg' => 'int', 'id_member' => 'int', 'id_topic' => 'int'), array($earlyMsg, $user_info['id'], $topic), array('id_member', 'id_topic'));
        redirectexit('board=' . $board . '.0');
    } else {
        $categories = array();
        $boards = array();
        if (isset($_REQUEST['c'])) {
            $_REQUEST['c'] = explode(',', $_REQUEST['c']);
            foreach ($_REQUEST['c'] as $c) {
                $categories[] = (int) $c;
            }
        }
        if (isset($_REQUEST['boards'])) {
            $_REQUEST['boards'] = explode(',', $_REQUEST['boards']);
            foreach ($_REQUEST['boards'] as $b) {
                $boards[] = (int) $b;
            }
        }
        if (!empty($board)) {
            $boards[] = (int) $board;
        }
        if (isset($_REQUEST['children']) && !empty($boards)) {
            // They want to mark the entire tree starting with the boards specified
            // The easist thing is to just get all the boards they can see, but since we've specified the top of tree we ignore some of them
            $request = $smcFunc['db_query']('', '
				SELECT b.id_board, b.id_parent
				FROM {db_prefix}boards AS b
				WHERE {query_see_board}
					AND b.child_level > {int:no_parents}
					AND b.id_board NOT IN ({array_int:board_list})
				ORDER BY child_level ASC
				', array('no_parents' => 0, 'board_list' => $boards));
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                if (in_array($row['id_parent'], $boards)) {
                    $boards[] = $row['id_board'];
                }
            }
            $smcFunc['db_free_result']($request);
        }
        $clauses = array();
        $clauseParameters = array();
        if (!empty($categories)) {
            $clauses[] = 'id_cat IN ({array_int:category_list})';
            $clauseParameters['category_list'] = $categories;
        }
        if (!empty($boards)) {
            $clauses[] = 'id_board IN ({array_int:board_list})';
            $clauseParameters['board_list'] = $boards;
        }
        if (empty($clauses)) {
            redirectexit();
        }
        $request = $smcFunc['db_query']('', '
			SELECT b.id_board
			FROM {db_prefix}boards AS b
			WHERE {query_see_board}
				AND b.' . implode(' OR b.', $clauses), array_merge($clauseParameters, array()));
        $boards = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $boards[] = $row['id_board'];
        }
        $smcFunc['db_free_result']($request);
        if (empty($boards)) {
            redirectexit();
        }
        markBoardsRead($boards, isset($_REQUEST['unread']));
        foreach ($boards as $b) {
            if (isset($_SESSION['topicseen_cache'][$b])) {
                $_SESSION['topicseen_cache'][$b] = array();
            }
        }
        if (!isset($_REQUEST['unread'])) {
            // Find all the boards this user can see.
            $result = $smcFunc['db_query']('', '
				SELECT b.id_board
				FROM {db_prefix}boards AS b
				WHERE b.id_parent IN ({array_int:parent_list})
					AND {query_see_board}', array('parent_list' => $boards));
            if ($smcFunc['db_num_rows']($result) > 0) {
                $logBoardInserts = '';
                while ($row = $smcFunc['db_fetch_assoc']($result)) {
                    $logBoardInserts[] = array($modSettings['maxMsgID'], $user_info['id'], $row['id_board']);
                }
                $smcFunc['db_insert']('replace', '{db_prefix}log_boards', array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'), $logBoardInserts, array('id_member', 'id_board'));
            }
            $smcFunc['db_free_result']($result);
            if (empty($board)) {
                redirectexit();
            } else {
                redirectexit('board=' . $board . '.0');
            }
        } else {
            if (empty($board_info['parent'])) {
                redirectexit();
            } else {
                redirectexit('board=' . $board_info['parent'] . '.0');
            }
        }
    }
}
Beispiel #4
0
/**
 * Moves one or more topics to a specific board.
 * Determines the source boards for the supplied topics
 * Handles the moving of mark_read data
 * Updates the posts count of the affected boards
 * This function doesn't check permissions.
 *
 * @param int[]|int $topics
 * @param int $toBoard
 */
function moveTopics($topics, $toBoard)
{
    global $user_info, $modSettings;
    $db = database();
    // Empty array?
    if (empty($topics)) {
        return;
    }
    // Only a single topic.
    if (is_numeric($topics)) {
        $topics = array($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 = $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 ($db->num_rows($request) == 0) {
        return;
    }
    while ($row = $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'];
        }
    }
    $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 = $db->query('', '
		SELECT lmr.id_member, lmr.id_msg, t.id_topic, IFNULL(lt.unwatched, 0) as unwatched
		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 = $db->fetch_assoc($request)) {
        $log_topics[] = array($row['id_member'], $row['id_topic'], $row['id_msg'], $row['unwatched']);
        // Prevent queries from getting too big. Taking some steam off.
        if (count($log_topics) > 500) {
            markTopicsRead($log_topics, true);
            $log_topics = array();
        }
    }
    $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!
        markTopicsRead($log_topics, true);
    }
    // Update the number of posts on each board.
    $totalTopics = 0;
    $totalPosts = 0;
    $totalUnapprovedTopics = 0;
    $totalUnapprovedPosts = 0;
    foreach ($fromBoards as $stats) {
        $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'];
    }
    $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
    $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 = $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 = $db->fetch_assoc($request)) {
            $approval_msgs[] = $row['id_msg'];
        }
        $db->free_result($request);
        // Empty the approval queue for these, as we're going to approve them next.
        if (!empty($approval_msgs)) {
            $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 = $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 = $db->fetch_assoc($request)) {
            $topicMaxMin[$row['id_topic']] = array('min' => $row['id_first_msg'], 'max' => $row['id_last_msg']);
        }
        $db->free_result($request);
        // Check the MAX and MIN are correct.
        $request = $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 = $db->fetch_assoc($request)) {
            // If not, update.
            if ($row['first_msg'] != $topicMaxMin[$row['id_topic']]['min'] || $row['last_msg'] != $topicMaxMin[$row['id_topic']]['max']) {
                $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']));
            }
        }
        $db->free_result($request);
    }
    $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));
    $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));
    $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 = $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) = $db->fetch_row($request);
    $db->free_result($request);
    if (!empty($isSeen) && !$user_info['is_guest']) {
        require_once SUBSDIR . '/Boards.subs.php';
        markBoardsRead($toBoard);
    }
    // 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 SUBSDIR . '/Post.subs.php';
    $updates = array_keys($fromBoards);
    $updates[] = $toBoard;
    updateLastMessages(array_unique($updates));
    // Update 'em pesky stats.
    updateTopicStats();
    updateStats('message');
    updateSettings(array('calendar_updated' => time()));
}
Beispiel #5
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;
}
 /**
  * Mark as read: boards, topics, unread replies.
  * Accessed by action=markasread
  * Subactions: sa=topic, sa=all, sa=unreadreplies
  */
 public function action_markasread()
 {
     global $board, $board_info;
     checkSession('get');
     require_once SUBSDIR . '/Boards.subs.php';
     $categories = array();
     $boards = array();
     if (isset($_REQUEST['c'])) {
         $_REQUEST['c'] = explode(',', $_REQUEST['c']);
         foreach ($_REQUEST['c'] as $c) {
             $categories[] = (int) $c;
         }
     }
     if (isset($_REQUEST['boards'])) {
         $_REQUEST['boards'] = explode(',', $_REQUEST['boards']);
         foreach ($_REQUEST['boards'] as $b) {
             $boards[] = (int) $b;
         }
     }
     if (!empty($board)) {
         $boards[] = (int) $board;
     }
     if (isset($_REQUEST['children']) && !empty($boards)) {
         // Mark all children of the boards we got (selected by the user).
         addChildBoards($boards);
     }
     $boards = array_keys(boardsPosts($boards, $categories));
     if (empty($boards)) {
         return '';
     }
     // Mark boards as read.
     markBoardsRead($boards, isset($_REQUEST['unread']), true);
     foreach ($boards as $b) {
         if (isset($_SESSION['topicseen_cache'][$b])) {
             $_SESSION['topicseen_cache'][$b] = array();
         }
     }
     $this->_querystring_board_limits = $_REQUEST['sa'] == 'board' ? ';boards=' . implode(',', $boards) . ';start=%d' : '';
     $sort_methods = array('subject', 'starter', 'replies', 'views', 'first_post', 'last_post');
     // The default is the most logical: newest first.
     if (!isset($_REQUEST['sort']) || !in_array($_REQUEST['sort'], $sort_methods)) {
         $this->_querystring_sort_limits = isset($_REQUEST['asc']) ? ';asc' : '';
     } else {
         $this->_querystring_sort_limits = ';sort=' . $_REQUEST['sort'] . (isset($_REQUEST['desc']) ? ';desc' : '');
     }
     if (!isset($_REQUEST['unread'])) {
         // Find all boards with the parents in the board list
         $boards_to_add = accessibleBoards(null, $boards);
         if (!empty($boards_to_add)) {
             markBoardsRead($boards_to_add);
         }
         if (empty($board)) {
             return '';
         } else {
             return 'board=' . $board . '.0';
         }
     } else {
         if (empty($board_info['parent'])) {
             return '';
         } else {
             return 'board=' . $board_info['parent'] . '.0';
         }
     }
 }
Beispiel #7
0
function MarkRead()
{
    global $board, $topic, $user_info, $board_info, $ID_MEMBER, $db_prefix, $modSettings;
    // No Guests allowed!
    is_not_guest();
    checkSession('get');
    if (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'all') {
        // Find all the boards this user can see.
        $result = db_query("\n\t\t\tSELECT b.ID_BOARD\n\t\t\tFROM {$db_prefix}boards AS b\n\t\t\tWHERE {$user_info['query_see_board']}", __FILE__, __LINE__);
        $boards = array();
        while ($row = mysql_fetch_assoc($result)) {
            $boards[] = $row['ID_BOARD'];
        }
        mysql_free_result($result);
        if (!empty($boards)) {
            markBoardsRead($boards, isset($_REQUEST['unread']));
        }
        $_SESSION['ID_MSG_LAST_VISIT'] = $modSettings['maxMsgID'];
        if (!empty($_SESSION['old_url']) && strpos($_SESSION['old_url'], 'action=unread') !== false) {
            redirectexit('action=unread');
        }
        if (isset($_SESSION['topicseen_cache'])) {
            $_SESSION['topicseen_cache'] = array();
        }
        redirectexit();
    } elseif (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'unreadreplies') {
        // Make sure all the boards are integers!
        $topics = explode('-', $_REQUEST['topics']);
        $setString = '';
        foreach ($topics as $ID_TOPIC) {
            $setString .= "\n\t\t\t\t({$modSettings['maxMsgID']}, {$ID_MEMBER}, " . (int) $ID_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__);
        if (isset($_SESSION['topicseen_cache'])) {
            $_SESSION['topicseen_cache'] = array();
        }
        redirectexit('action=unreadreplies');
    } elseif (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'topic') {
        if (!empty($_GET['t'])) {
            // Get the latest message before this one.
            $result = db_query("\n\t\t\t\tSELECT MAX(ID_MSG)\n\t\t\t\tFROM {$db_prefix}messages\n\t\t\t\tWHERE ID_TOPIC = {$topic}\n\t\t\t\t\tAND ID_MSG < " . (int) $_GET['t'], __FILE__, __LINE__);
            list($earlyMsg) = mysql_fetch_row($result);
            mysql_free_result($result);
        }
        if (empty($earlyMsg)) {
            $result = db_query("\n\t\t\t\tSELECT ID_MSG\n\t\t\t\tFROM {$db_prefix}messages\n\t\t\t\tWHERE ID_TOPIC = {$topic}\n\t\t\t\tORDER BY ID_MSG\n\t\t\t\tLIMIT " . (int) $_REQUEST['start'] . ", 1", __FILE__, __LINE__);
            list($earlyMsg) = mysql_fetch_row($result);
            mysql_free_result($result);
        }
        $earlyMsg--;
        // Use a time one second earlier than the first time: blam, unread!
        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 ({$earlyMsg}, {$ID_MEMBER}, {$topic})", __FILE__, __LINE__);
        redirectexit('board=' . $board . '.0');
    } else {
        $categories = array();
        $boards = array();
        if (isset($_REQUEST['c'])) {
            $_REQUEST['c'] = explode(',', $_REQUEST['c']);
            foreach ($_REQUEST['c'] as $c) {
                $categories[] = (int) $c;
            }
        }
        if (isset($_REQUEST['boards'])) {
            $_REQUEST['boards'] = explode(',', $_REQUEST['boards']);
            foreach ($_REQUEST['boards'] as $b) {
                $boards[] = (int) $b;
            }
        }
        if (!empty($board)) {
            $boards[] = (int) $board;
        }
        $clauses = array();
        if (!empty($categories)) {
            $clauses[] = "ID_CAT IN (" . implode(', ', $categories) . ")";
        }
        if (!empty($boards)) {
            $clauses[] = "ID_BOARD IN (" . implode(', ', $boards) . ")";
        }
        if (empty($clauses)) {
            redirectexit();
        }
        $request = db_query("\n\t\t\tSELECT b.ID_BOARD\n\t\t\tFROM {$db_prefix}boards AS b\n\t\t\tWHERE {$user_info['query_see_board']}\n\t\t\t\tAND b." . implode(" OR b.", $clauses), __FILE__, __LINE__);
        $boards = array();
        while ($row = mysql_fetch_assoc($request)) {
            $boards[] = $row['ID_BOARD'];
        }
        mysql_free_result($request);
        if (empty($boards)) {
            redirectexit();
        }
        markBoardsRead($boards, isset($_REQUEST['unread']));
        foreach ($boards as $b) {
            if (isset($_SESSION['topicseen_cache'][$b])) {
                $_SESSION['topicseen_cache'][$b] = array();
            }
        }
        if (!isset($_REQUEST['unread'])) {
            // Find all the boards this user can see.
            $result = db_query("\n\t\t\t\tSELECT b.ID_BOARD\n\t\t\t\tFROM {$db_prefix}boards AS b\n\t\t\t\tWHERE b.ID_PARENT IN (" . implode(', ', $boards) . ")\n\t\t\t\t\tAND {$user_info['query_see_board']}", __FILE__, __LINE__);
            if (mysql_num_rows($result) > 0) {
                $setString = '';
                while ($row = mysql_fetch_assoc($result)) {
                    $setString .= "\n\t\t\t\t\t\t({$modSettings['maxMsgID']}, {$ID_MEMBER}, {$row['ID_BOARD']}),";
                }
                db_query("\n\t\t\t\t\tREPLACE INTO {$db_prefix}log_boards\n\t\t\t\t\t\t(ID_MSG, ID_MEMBER, ID_BOARD)\n\t\t\t\t\tVALUES" . substr($setString, 0, -1), __FILE__, __LINE__);
            }
            mysql_free_result($result);
            if (empty($board)) {
                redirectexit();
            } else {
                redirectexit('board=' . $board . '.0');
            }
        } else {
            if (empty($board_info['parent'])) {
                redirectexit();
            } else {
                redirectexit('board=' . $board_info['parent'] . '.0');
            }
        }
    }
}
Beispiel #8
0
    /**
     * Posts or saves the message composed with Post().
     *
     * requires various permissions depending on the action.
     * handles attachment, post, and calendar saving.
     * sends off notifications, and allows for announcements and moderation.
     * accessed from ?action=post2.
     */
    public function action_post2()
    {
        global $board, $topic, $txt, $modSettings, $context, $user_settings;
        global $user_info, $board_info, $options, $ignore_temp;
        // Sneaking off, are we?
        if (empty($_POST) && empty($topic)) {
            if (empty($_SERVER['CONTENT_LENGTH'])) {
                redirectexit('action=post;board=' . $board . '.0');
            } else {
                fatal_lang_error('post_upload_error', false);
            }
        } elseif (empty($_POST) && !empty($topic)) {
            redirectexit('action=post;topic=' . $topic . '.0');
        }
        // No need!
        $context['robot_no_index'] = true;
        // We are now in post2 action
        $context['current_action'] = 'post2';
        require_once SOURCEDIR . '/AttachmentErrorContext.class.php';
        // No errors as yet.
        $post_errors = Error_Context::context('post', 1);
        $attach_errors = Attachment_Error_Context::context();
        // If the session has timed out, let the user re-submit their form.
        if (checkSession('post', '', false) != '') {
            $post_errors->addError('session_timeout');
            // Disable the preview so that any potentially malicious code is not executed
            $_REQUEST['preview'] = false;
            return $this->action_post();
        }
        // Wrong verification code?
        if (!$user_info['is_admin'] && !$user_info['is_mod'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || $user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1)) {
            require_once SUBSDIR . '/VerificationControls.class.php';
            $verificationOptions = array('id' => 'post');
            $context['require_verification'] = create_control_verification($verificationOptions, true);
            if (is_array($context['require_verification'])) {
                foreach ($context['require_verification'] as $verification_error) {
                    $post_errors->addError($verification_error);
                }
            }
        }
        require_once SUBSDIR . '/Boards.subs.php';
        require_once SUBSDIR . '/Post.subs.php';
        loadLanguage('Post');
        // Drafts enabled and needed?
        if (!empty($modSettings['drafts_enabled']) && (isset($_POST['save_draft']) || isset($_POST['id_draft']))) {
            require_once SUBSDIR . '/Drafts.subs.php';
        }
        // First check to see if they are trying to delete any current attachments.
        if (isset($_POST['attach_del'])) {
            $keep_temp = array();
            $keep_ids = array();
            foreach ($_POST['attach_del'] as $dummy) {
                if (strpos($dummy, 'post_tmp_' . $user_info['id']) !== false) {
                    $keep_temp[] = $dummy;
                } else {
                    $keep_ids[] = (int) $dummy;
                }
            }
            if (isset($_SESSION['temp_attachments'])) {
                foreach ($_SESSION['temp_attachments'] as $attachID => $attachment) {
                    if (isset($_SESSION['temp_attachments']['post']['files'], $attachment['name']) && in_array($attachment['name'], $_SESSION['temp_attachments']['post']['files']) || in_array($attachID, $keep_temp) || strpos($attachID, 'post_tmp_' . $user_info['id']) === false) {
                        continue;
                    }
                    unset($_SESSION['temp_attachments'][$attachID]);
                    @unlink($attachment['tmp_name']);
                }
            }
            if (!empty($_REQUEST['msg'])) {
                require_once SUBSDIR . '/ManageAttachments.subs.php';
                $attachmentQuery = array('attachment_type' => 0, 'id_msg' => (int) $_REQUEST['msg'], 'not_id_attach' => $keep_ids);
                removeAttachments($attachmentQuery);
            }
        }
        // Then try to upload any attachments.
        $context['attachments']['can']['post'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || $modSettings['postmod_active'] && allowedTo('post_unapproved_attachments'));
        if ($context['attachments']['can']['post'] && empty($_POST['from_qr'])) {
            require_once SUBSDIR . '/Attachments.subs.php';
            if (isset($_REQUEST['msg'])) {
                processAttachments((int) $_REQUEST['msg']);
            } else {
                processAttachments();
            }
        }
        // Previewing? Go back to start.
        if (isset($_REQUEST['preview'])) {
            return $this->action_post();
        }
        // Prevent double submission of this form.
        checkSubmitOnce('check');
        // If this isn't a new topic load the topic info that we need.
        if (!empty($topic)) {
            require_once SUBSDIR . '/Topic.subs.php';
            $topic_info = getTopicInfo($topic);
            // Though the topic should be there, it might have vanished.
            if (empty($topic_info)) {
                fatal_lang_error('topic_doesnt_exist');
            }
            // Did this topic suddenly move? Just checking...
            if ($topic_info['id_board'] != $board) {
                fatal_lang_error('not_a_topic');
            }
        }
        // Replying to a topic?
        if (!empty($topic) && !isset($_REQUEST['msg'])) {
            // Don't allow a post if it's locked.
            if ($topic_info['locked'] != 0 && !allowedTo('moderate_board')) {
                fatal_lang_error('topic_locked', false);
            }
            // Sorry, multiple polls aren't allowed... yet.  You should stop giving me ideas :P.
            if (isset($_REQUEST['poll']) && $topic_info['id_poll'] > 0) {
                unset($_REQUEST['poll']);
            }
            // Do the permissions and approval stuff...
            $becomesApproved = true;
            if ($topic_info['id_member_started'] != $user_info['id']) {
                if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any')) {
                    $becomesApproved = false;
                } else {
                    isAllowedTo('post_reply_any');
                }
            } elseif (!allowedTo('post_reply_any')) {
                if ($modSettings['postmod_active']) {
                    if (allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own')) {
                        $becomesApproved = false;
                    } elseif ($user_info['is_guest'] && allowedTo('post_unapproved_replies_any')) {
                        $becomesApproved = false;
                    } else {
                        isAllowedTo('post_reply_own');
                    }
                }
            }
            if (isset($_POST['lock'])) {
                // Nothing is changed to the lock.
                if (empty($topic_info['locked']) && empty($_POST['lock']) || !empty($_POST['lock']) && !empty($topic_info['locked'])) {
                    unset($_POST['lock']);
                } elseif (!allowedTo(array('lock_any', 'lock_own')) || !allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']) {
                    unset($_POST['lock']);
                } elseif (!allowedTo('lock_any')) {
                    // You cannot override a moderator lock.
                    if ($topic_info['locked'] == 1) {
                        unset($_POST['lock']);
                    } else {
                        $_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
                    }
                } else {
                    $_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
                }
            }
            // So you wanna (un)sticky this...let's see.
            if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || $_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky'))) {
                unset($_POST['sticky']);
            }
            // If drafts are enabled, then pass this off
            if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft'])) {
                saveDraft();
                return $this->action_post();
            }
            // If the number of replies has changed, if the setting is enabled, go back to action_post() - which handles the error.
            if (empty($options['no_new_reply_warning']) && isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg']) {
                addInlineJavascript('
					$(document).ready(function () {
						$("html,body").scrollTop($(\'.category_header:visible:first\').offset().top);
					});');
                return $this->action_post();
            }
            $posterIsGuest = $user_info['is_guest'];
        } elseif (empty($topic)) {
            // Now don't be silly, new topics will get their own id_msg soon enough.
            unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
            // Do like, the permissions, for safety and stuff...
            $becomesApproved = true;
            if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics')) {
                $becomesApproved = false;
            } else {
                isAllowedTo('post_new');
            }
            if (isset($_POST['lock'])) {
                // New topics are by default not locked.
                if (empty($_POST['lock'])) {
                    unset($_POST['lock']);
                } elseif (!allowedTo(array('lock_any', 'lock_own'))) {
                    unset($_POST['lock']);
                } else {
                    $_POST['lock'] = allowedTo('lock_any') ? 1 : 2;
                }
            }
            if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || empty($_POST['sticky']) || !allowedTo('make_sticky'))) {
                unset($_POST['sticky']);
            }
            // Saving your new topic as a draft first?
            if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft'])) {
                saveDraft();
                return $this->action_post();
            }
            $posterIsGuest = $user_info['is_guest'];
        } elseif (isset($_REQUEST['msg']) && !empty($topic)) {
            $_REQUEST['msg'] = (int) $_REQUEST['msg'];
            require_once SUBSDIR . '/Messages.subs.php';
            $msgInfo = basicMessageInfo($_REQUEST['msg'], true);
            if (empty($msgInfo)) {
                fatal_lang_error('cant_find_messages', false);
            }
            if (!empty($topic_info['locked']) && !allowedTo('moderate_board')) {
                fatal_lang_error('topic_locked', false);
            }
            if (isset($_POST['lock'])) {
                // Nothing changes to the lock status.
                if (empty($_POST['lock']) && empty($topic_info['locked']) || !empty($_POST['lock']) && !empty($topic_info['locked'])) {
                    unset($_POST['lock']);
                } elseif (!allowedTo(array('lock_any', 'lock_own')) || !allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']) {
                    unset($_POST['lock']);
                } elseif (!allowedTo('lock_any')) {
                    // You're not allowed to break a moderator's lock.
                    if ($topic_info['locked'] == 1) {
                        unset($_POST['lock']);
                    } else {
                        $_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
                    }
                } else {
                    $_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
                }
            }
            // Change the sticky status of this topic?
            if (isset($_POST['sticky']) && (!allowedTo('make_sticky') || $_POST['sticky'] == $topic_info['is_sticky'])) {
                unset($_POST['sticky']);
            }
            if ($msgInfo['id_member'] == $user_info['id'] && !allowedTo('modify_any')) {
                if ((!$modSettings['postmod_active'] || $msgInfo['approved']) && !empty($modSettings['edit_disable_time']) && $msgInfo['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time()) {
                    fatal_lang_error('modify_post_time_passed', false);
                } elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_own')) {
                    isAllowedTo('modify_replies');
                } else {
                    isAllowedTo('modify_own');
                }
            } elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_any')) {
                isAllowedTo('modify_replies');
                // If you're modifying a reply, I say it better be logged...
                $moderationAction = true;
            } else {
                isAllowedTo('modify_any');
                // Log it, assuming you're not modifying your own post.
                if ($msgInfo['id_member'] != $user_info['id']) {
                    $moderationAction = true;
                }
            }
            // If drafts are enabled, then lets send this off to save
            if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft'])) {
                saveDraft();
                return $this->action_post();
            }
            $posterIsGuest = empty($msgInfo['id_member']);
            // Can they approve it?
            $can_approve = allowedTo('approve_posts');
            $becomesApproved = $modSettings['postmod_active'] ? $can_approve && !$msgInfo['approved'] ? !empty($_REQUEST['approve']) ? 1 : 0 : $msgInfo['approved'] : 1;
            $approve_has_changed = $msgInfo['approved'] != $becomesApproved;
            if (!allowedTo('moderate_forum') || !$posterIsGuest) {
                $_POST['guestname'] = $msgInfo['poster_name'];
                $_POST['email'] = $msgInfo['poster_email'];
            }
        }
        // In case we want to override
        if (allowedTo('approve_posts')) {
            $becomesApproved = !isset($_REQUEST['approve']) || !empty($_REQUEST['approve']) ? 1 : 0;
            $approve_has_changed = isset($msgInfo['approved']) ? $msgInfo['approved'] != $becomesApproved : false;
        }
        // If the poster is a guest evaluate the legality of name and email.
        if ($posterIsGuest) {
            $_POST['guestname'] = !isset($_POST['guestname']) ? '' : Util::htmlspecialchars(trim($_POST['guestname']));
            $_POST['email'] = !isset($_POST['email']) ? '' : Util::htmlspecialchars(trim($_POST['email']));
            if ($_POST['guestname'] == '' || $_POST['guestname'] == '_') {
                $post_errors->addError('no_name');
            }
            if (Util::strlen($_POST['guestname']) > 25) {
                $post_errors->addError('long_name');
            }
            if (empty($modSettings['guest_post_no_email'])) {
                // Only check if they changed it!
                if (!isset($msgInfo) || $msgInfo['poster_email'] != $_POST['email']) {
                    require_once SUBSDIR . '/DataValidator.class.php';
                    if (!allowedTo('moderate_forum') && !Data_Validator::is_valid($_POST, array('email' => 'valid_email|required'), array('email' => 'trim'))) {
                        empty($_POST['email']) ? $post_errors->addError('no_email') : $post_errors->addError('bad_email');
                    }
                }
                // Now make sure this email address is not banned from posting.
                isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
            }
            // In case they are making multiple posts this visit, help them along by storing their name.
            if (!$post_errors->hasErrors()) {
                $_SESSION['guest_name'] = $_POST['guestname'];
                $_SESSION['guest_email'] = $_POST['email'];
            }
        }
        // Check the subject and message.
        if (!isset($_POST['subject']) || Util::htmltrim(Util::htmlspecialchars($_POST['subject'])) === '') {
            $post_errors->addError('no_subject');
        }
        if (!isset($_POST['message']) || Util::htmltrim(Util::htmlspecialchars($_POST['message'], ENT_QUOTES)) === '') {
            $post_errors->addError('no_message');
        } elseif (!empty($modSettings['max_messageLength']) && Util::strlen($_POST['message']) > $modSettings['max_messageLength']) {
            $post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));
        } else {
            // Prepare the message a bit for some additional testing.
            $_POST['message'] = Util::htmlspecialchars($_POST['message'], ENT_QUOTES);
            // Preparse code. (Zef)
            if ($user_info['is_guest']) {
                $user_info['name'] = $_POST['guestname'];
            }
            preparsecode($_POST['message']);
            // Let's see if there's still some content left without the tags.
            if (Util::htmltrim(strip_tags(parse_bbc($_POST['message'], false), '<img>')) === '' && (!allowedTo('admin_forum') || strpos($_POST['message'], '[html]') === false)) {
                $post_errors->addError('no_message');
            }
        }
        if (isset($_POST['calendar']) && !isset($_REQUEST['deleteevent']) && Util::htmltrim($_POST['evtitle']) === '') {
            $post_errors->addError('no_event');
        }
        // Validate the poll...
        if (isset($_REQUEST['poll']) && !empty($modSettings['pollMode'])) {
            if (!empty($topic) && !isset($_REQUEST['msg'])) {
                fatal_lang_error('no_access', false);
            }
            // This is a new topic... so it's a new poll.
            if (empty($topic)) {
                isAllowedTo('poll_post');
            } elseif ($user_info['id'] == $topic_info['id_member_started'] && !allowedTo('poll_add_any')) {
                isAllowedTo('poll_add_own');
            } else {
                isAllowedTo('poll_add_any');
            }
            if (!isset($_POST['question']) || trim($_POST['question']) == '') {
                $post_errors->addError('no_question');
            }
            $_POST['options'] = empty($_POST['options']) ? array() : htmltrim__recursive($_POST['options']);
            // Get rid of empty ones.
            foreach ($_POST['options'] as $k => $option) {
                if ($option == '') {
                    unset($_POST['options'][$k], $_POST['options'][$k]);
                }
            }
            // What are you going to vote between with one choice?!?
            if (count($_POST['options']) < 2) {
                $post_errors->addError('poll_few');
            } elseif (count($_POST['options']) > 256) {
                $post_errors->addError('poll_many');
            }
        }
        if ($posterIsGuest) {
            // If user is a guest, make sure the chosen name isn't taken.
            require_once SUBSDIR . '/Members.subs.php';
            if (isReservedName($_POST['guestname'], 0, true, false) && (!isset($msgInfo['poster_name']) || $_POST['guestname'] != $msgInfo['poster_name'])) {
                $post_errors->addError('bad_name');
            }
        } elseif (!isset($_REQUEST['msg'])) {
            $_POST['guestname'] = $user_info['username'];
            $_POST['email'] = $user_info['email'];
        }
        // Posting somewhere else? Are we sure you can?
        if (!empty($_REQUEST['post_in_board'])) {
            $new_board = (int) $_REQUEST['post_in_board'];
            if (!allowedTo('post_new', $new_board)) {
                $post_in_board = boardInfo($new_board);
                if (!empty($post_in_board)) {
                    $post_errors->addError(array('post_new_board', array($post_in_board['name'])));
                } else {
                    $post_errors->addError('post_new');
                }
            }
        }
        // Any mistakes?
        if ($post_errors->hasErrors() || $attach_errors->hasErrors()) {
            addInlineJavascript('
				$(document).ready(function () {
					$("html,body").scrollTop($(\'.category_header:visible:first\').offset().top);
				});');
            return $this->action_post();
        }
        // Make sure the user isn't spamming the board.
        if (!isset($_REQUEST['msg'])) {
            spamProtection('post');
        }
        // At about this point, we're posting and that's that.
        ignore_user_abort(true);
        @set_time_limit(300);
        // Add special html entities to the subject, name, and email.
        $_POST['subject'] = strtr(Util::htmlspecialchars($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
        $_POST['guestname'] = htmlspecialchars($_POST['guestname'], ENT_COMPAT, 'UTF-8');
        $_POST['email'] = htmlspecialchars($_POST['email'], ENT_COMPAT, 'UTF-8');
        // At this point, we want to make sure the subject isn't too long.
        if (Util::strlen($_POST['subject']) > 100) {
            $_POST['subject'] = Util::substr($_POST['subject'], 0, 100);
        }
        if (!empty($modSettings['mentions_enabled']) && !empty($_REQUEST['uid'])) {
            $query_params = array();
            $query_params['member_ids'] = array_unique(array_map('intval', $_REQUEST['uid']));
            require_once SUBSDIR . '/Members.subs.php';
            $mentioned_members = membersBy('member_ids', $query_params, true);
            $replacements = 0;
            $actually_mentioned = array();
            foreach ($mentioned_members as $member) {
                $_POST['message'] = str_replace('@' . $member['real_name'], '[member=' . $member['id_member'] . ']' . $member['real_name'] . '[/member]', $_POST['message'], $replacements);
                if ($replacements > 0) {
                    $actually_mentioned[] = $member['id_member'];
                }
            }
        }
        // Make the poll...
        if (isset($_REQUEST['poll'])) {
            // Make sure that the user has not entered a ridiculous number of options..
            if (empty($_POST['poll_max_votes']) || $_POST['poll_max_votes'] <= 0) {
                $_POST['poll_max_votes'] = 1;
            } elseif ($_POST['poll_max_votes'] > count($_POST['options'])) {
                $_POST['poll_max_votes'] = count($_POST['options']);
            } else {
                $_POST['poll_max_votes'] = (int) $_POST['poll_max_votes'];
            }
            $_POST['poll_expire'] = (int) $_POST['poll_expire'];
            $_POST['poll_expire'] = $_POST['poll_expire'] > 9999 ? 9999 : ($_POST['poll_expire'] < 0 ? 0 : $_POST['poll_expire']);
            // Just set it to zero if it's not there..
            if (!isset($_POST['poll_hide'])) {
                $_POST['poll_hide'] = 0;
            } else {
                $_POST['poll_hide'] = (int) $_POST['poll_hide'];
            }
            $_POST['poll_change_vote'] = isset($_POST['poll_change_vote']) ? 1 : 0;
            $_POST['poll_guest_vote'] = isset($_POST['poll_guest_vote']) ? 1 : 0;
            // Make sure guests are actually allowed to vote generally.
            if ($_POST['poll_guest_vote']) {
                require_once SUBSDIR . '/Members.subs.php';
                $allowedVoteGroups = groupsAllowedTo('poll_vote', $board);
                if (!in_array(-1, $allowedVoteGroups['allowed'])) {
                    $_POST['poll_guest_vote'] = 0;
                }
            }
            // If the user tries to set the poll too far in advance, don't let them.
            if (!empty($_POST['poll_expire']) && $_POST['poll_expire'] < 1) {
                fatal_lang_error('poll_range_error', false);
            } elseif (empty($_POST['poll_expire']) && $_POST['poll_hide'] == 2) {
                $_POST['poll_hide'] = 1;
            }
            // Clean up the question and answers.
            $_POST['question'] = htmlspecialchars($_POST['question'], ENT_COMPAT, 'UTF-8');
            $_POST['question'] = Util::substr($_POST['question'], 0, 255);
            $_POST['question'] = preg_replace('~&amp;#(\\d{4,5}|[2-9]\\d{2,4}|1[2-9]\\d);~', '&#$1;', $_POST['question']);
            $_POST['options'] = htmlspecialchars__recursive($_POST['options']);
            // Finally, make the poll.
            require_once SUBSDIR . '/Poll.subs.php';
            $id_poll = createPoll($_POST['question'], $user_info['id'], $_POST['guestname'], $_POST['poll_max_votes'], $_POST['poll_hide'], $_POST['poll_expire'], $_POST['poll_change_vote'], $_POST['poll_guest_vote'], $_POST['options']);
        } else {
            $id_poll = 0;
        }
        // ...or attach a new file...
        if (empty($ignore_temp) && $context['attachments']['can']['post'] && !empty($_SESSION['temp_attachments']) && empty($_POST['from_qr'])) {
            $attachIDs = array();
            foreach ($_SESSION['temp_attachments'] as $attachID => $attachment) {
                if ($attachID != 'initial_error' && strpos($attachID, 'post_tmp_' . $user_info['id']) === false) {
                    continue;
                }
                // If there was an initial error just show that message.
                if ($attachID == 'initial_error') {
                    unset($_SESSION['temp_attachments']);
                    break;
                }
                // No errors, then try to create the attachment
                if (empty($attachment['errors'])) {
                    // Load the attachmentOptions array with the data needed to create an attachment
                    $attachmentOptions = array('post' => isset($_REQUEST['msg']) ? $_REQUEST['msg'] : 0, 'poster' => $user_info['id'], 'name' => $attachment['name'], 'tmp_name' => $attachment['tmp_name'], 'size' => isset($attachment['size']) ? $attachment['size'] : 0, 'mime_type' => isset($attachment['type']) ? $attachment['type'] : '', 'id_folder' => isset($attachment['id_folder']) ? $attachment['id_folder'] : 0, 'approved' => !$modSettings['postmod_active'] || allowedTo('post_attachment'), 'errors' => array());
                    if (createAttachment($attachmentOptions)) {
                        $attachIDs[] = $attachmentOptions['id'];
                        if (!empty($attachmentOptions['thumb'])) {
                            $attachIDs[] = $attachmentOptions['thumb'];
                        }
                    }
                } else {
                    @unlink($attachment['tmp_name']);
                }
            }
            unset($_SESSION['temp_attachments']);
        }
        // Creating a new topic?
        $newTopic = empty($_REQUEST['msg']) && empty($topic);
        $_POST['icon'] = !empty($attachIDs) && $_POST['icon'] == 'xx' ? 'clip' : $_POST['icon'];
        // Collect all parameters for the creation or modification of a post.
        $msgOptions = array('id' => empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg'], 'subject' => $_POST['subject'], 'body' => $_POST['message'], 'icon' => preg_replace('~[\\./\\\\*:"\'<>]~', '', $_POST['icon']), 'smileys_enabled' => !isset($_POST['ns']), 'attachments' => empty($attachIDs) ? array() : $attachIDs, 'approved' => $becomesApproved);
        $topicOptions = array('id' => empty($topic) ? 0 : $topic, 'board' => $board, 'poll' => isset($_REQUEST['poll']) ? $id_poll : null, 'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null, 'sticky_mode' => isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics']) ? (int) $_POST['sticky'] : null, 'mark_as_read' => true, 'is_approved' => !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']));
        $posterOptions = array('id' => $user_info['id'], 'name' => $_POST['guestname'], 'email' => $_POST['email'], 'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count']);
        // This is an already existing message. Edit it.
        if (!empty($_REQUEST['msg'])) {
            // Have admins allowed people to hide their screwups?
            if (time() - $msgInfo['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $msgInfo['id_member']) {
                $msgOptions['modify_time'] = time();
                $msgOptions['modify_name'] = $user_info['name'];
            }
            // This will save some time...
            if (empty($approve_has_changed)) {
                unset($msgOptions['approved']);
            }
            modifyPost($msgOptions, $topicOptions, $posterOptions);
        } else {
            if (!empty($modSettings['enableFollowup']) && !empty($_REQUEST['followup'])) {
                $original_post = (int) $_REQUEST['followup'];
            }
            // We also have to fake the board:
            // if it's valid and it's not the current, let's forget about the "current" and load the new one
            if (!empty($new_board) && $board !== $new_board) {
                $board = $new_board;
                loadBoard();
                // Some details changed
                $topicOptions['board'] = $board;
                $topicOptions['is_approved'] = !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']);
                $posterOptions['update_post_count'] = !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'];
            }
            createPost($msgOptions, $topicOptions, $posterOptions);
            if (isset($topicOptions['id'])) {
                $topic = $topicOptions['id'];
            }
            if (!empty($modSettings['enableFollowup'])) {
                require_once SUBSDIR . '/FollowUps.subs.php';
                require_once SUBSDIR . '/Messages.subs.php';
                // Time to update the original message with a pointer to the new one
                if (!empty($original_post) && canAccessMessage($original_post)) {
                    linkMessages($original_post, $topic);
                }
            }
        }
        // If we had a draft for this, its time to remove it since it was just posted
        if (!empty($modSettings['drafts_enabled']) && !empty($_POST['id_draft'])) {
            deleteDrafts($_POST['id_draft'], $user_info['id']);
        }
        // Editing or posting an event?
        if (isset($_POST['calendar']) && (!isset($_REQUEST['eventid']) || $_REQUEST['eventid'] == -1)) {
            require_once SUBSDIR . '/Calendar.subs.php';
            // Make sure they can link an event to this post.
            canLinkEvent();
            // Insert the event.
            $eventOptions = array('id_board' => $board, 'id_topic' => $topic, 'title' => $_POST['evtitle'], 'member' => $user_info['id'], 'start_date' => sprintf('%04d-%02d-%02d', $_POST['year'], $_POST['month'], $_POST['day']), 'span' => isset($_POST['span']) && $_POST['span'] > 0 ? min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1) : 0);
            insertEvent($eventOptions);
        } elseif (isset($_POST['calendar'])) {
            $_REQUEST['eventid'] = (int) $_REQUEST['eventid'];
            // Validate the post...
            require_once SUBSDIR . '/Calendar.subs.php';
            validateEventPost();
            // If you're not allowed to edit any events, you have to be the poster.
            if (!allowedTo('calendar_edit_any')) {
                $event_poster = getEventPoster($_REQUEST['eventid']);
                // Silly hacker, Trix are for kids. ...probably trademarked somewhere, this is FAIR USE! (parody...)
                isAllowedTo('calendar_edit_' . ($event_poster == $user_info['id'] ? 'own' : 'any'));
            }
            // Delete it?
            if (isset($_REQUEST['deleteevent'])) {
                removeEvent($_REQUEST['eventid']);
            } else {
                $span = !empty($modSettings['cal_allowspan']) && !empty($_REQUEST['span']) ? min((int) $modSettings['cal_maxspan'], (int) $_REQUEST['span'] - 1) : 0;
                $start_time = mktime(0, 0, 0, (int) $_REQUEST['month'], (int) $_REQUEST['day'], (int) $_REQUEST['year']);
                $eventOptions = array('start_date' => strftime('%Y-%m-%d', $start_time), 'end_date' => strftime('%Y-%m-%d', $start_time + $span * 86400), 'title' => $_REQUEST['evtitle']);
                modifyEvent($_REQUEST['eventid'], $eventOptions);
            }
        }
        // Marking boards as read.
        // (You just posted and they will be unread.)
        if (!$user_info['is_guest']) {
            $board_list = !empty($board_info['parent_boards']) ? array_keys($board_info['parent_boards']) : array();
            // Returning to the topic?
            if (!empty($_REQUEST['goback'])) {
                $board_list[] = $board;
            }
            if (!empty($board_list)) {
                markBoardsRead($board_list, false, false);
            }
        }
        // Turn notification on or off.
        if (!empty($_POST['notify']) && allowedTo('mark_any_notify')) {
            setTopicNotification($user_info['id'], $topic, true);
        } elseif (!$newTopic) {
            setTopicNotification($user_info['id'], $topic, false);
        }
        // Log an act of moderation - modifying.
        if (!empty($moderationAction)) {
            logAction('modify', array('topic' => $topic, 'message' => (int) $_REQUEST['msg'], 'member' => $msgInfo['id_member'], 'board' => $board));
        }
        if (isset($_POST['lock']) && $_POST['lock'] != 2) {
            logAction(empty($_POST['lock']) ? 'unlock' : 'lock', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
        }
        if (isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics'])) {
            logAction(empty($_POST['sticky']) ? 'unsticky' : 'sticky', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
        }
        // Notify any members who have notification turned on for this topic/board - only do this if it's going to be approved(!)
        if ($becomesApproved) {
            require_once SUBSDIR . '/Notification.subs.php';
            if ($newTopic) {
                $notifyData = array('body' => $_POST['message'], 'subject' => $_POST['subject'], 'name' => $user_info['name'], 'poster' => $user_info['id'], 'msg' => $msgOptions['id'], 'board' => $board, 'topic' => $topic, 'signature' => isset($user_settings['signature']) ? $user_settings['signature'] : '');
                sendBoardNotifications($notifyData);
            } elseif (empty($_REQUEST['msg'])) {
                // Only send it to everyone if the topic is approved, otherwise just to the topic starter if they want it.
                if ($topic_info['approved']) {
                    sendNotifications($topic, 'reply');
                } else {
                    sendNotifications($topic, 'reply', array(), $topic_info['id_member_started']);
                }
            }
        }
        if (!empty($modSettings['mentions_enabled']) && !empty($actually_mentioned)) {
            require_once CONTROLLERDIR . '/Mentions.controller.php';
            $mentions = new Mentions_Controller();
            $mentions->setData(array('id_member' => $actually_mentioned, 'type' => 'men', 'id_msg' => $msgOptions['id'], 'status' => $becomesApproved ? 'new' : 'unapproved'));
            $mentions->action_add();
        }
        if ($board_info['num_topics'] == 0) {
            cache_put_data('board-' . $board, null, 120);
        }
        if (!empty($_POST['announce_topic'])) {
            redirectexit('action=announce;sa=selectgroup;topic=' . $topic . (!empty($_POST['move']) && allowedTo('move_any') ? ';move' : '') . (empty($_REQUEST['goback']) ? '' : ';goback'));
        }
        if (!empty($_POST['move']) && allowedTo('move_any')) {
            redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
        }
        // Return to post if the mod is on.
        if (isset($_REQUEST['msg']) && !empty($_REQUEST['goback'])) {
            redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg'], isBrowser('ie'));
        } elseif (!empty($_REQUEST['goback'])) {
            redirectexit('topic=' . $topic . '.new#new', isBrowser('ie'));
        } else {
            redirectexit('board=' . $board . '.0');
        }
    }
 /**
  * Show the list of topics in this board, along with any sub-boards.
  * @uses MessageIndex template topic_listing sub template
  */
 public function action_messageindex()
 {
     global $txt, $scripturl, $board, $modSettings, $context;
     global $options, $settings, $board_info, $user_info;
     // Fairly often, we'll work with boards. Current board, sub-boards.
     require_once SUBSDIR . '/Boards.subs.php';
     // If this is a redirection board head off.
     if ($board_info['redirect']) {
         incrementBoard($board, 'num_posts');
         redirectexit($board_info['redirect']);
     }
     loadTemplate('MessageIndex');
     loadJavascriptFile('topic.js');
     $context['name'] = $board_info['name'];
     $context['sub_template'] = 'topic_listing';
     $context['description'] = $board_info['description'];
     $template_layers = Template_Layers::getInstance();
     // How many topics do we have in total?
     $board_info['total_topics'] = allowedTo('approve_posts') ? $board_info['num_topics'] + $board_info['unapproved_topics'] : $board_info['num_topics'] + $board_info['unapproved_user_topics'];
     // View all the topics, or just a few?
     $context['topics_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page']) ? $options['topics_per_page'] : $modSettings['defaultMaxTopics'];
     $context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
     $maxindex = isset($_REQUEST['all']) && !empty($modSettings['enableAllMessages']) ? $board_info['total_topics'] : $context['topics_per_page'];
     // Right, let's only index normal stuff!
     if (count($_GET) > 1) {
         $session_name = session_name();
         foreach ($_GET as $k => $v) {
             if (!in_array($k, array('board', 'start', $session_name))) {
                 $context['robot_no_index'] = true;
             }
         }
     }
     if (!empty($_REQUEST['start']) && (!is_numeric($_REQUEST['start']) || $_REQUEST['start'] % $context['messages_per_page'] != 0)) {
         $context['robot_no_index'] = true;
     }
     // If we can view unapproved messages and there are some build up a list.
     if (allowedTo('approve_posts') && ($board_info['unapproved_topics'] || $board_info['unapproved_posts'])) {
         $untopics = $board_info['unapproved_topics'] ? '<a href="' . $scripturl . '?action=moderate;area=postmod;sa=topics;brd=' . $board . '">' . $board_info['unapproved_topics'] . '</a>' : 0;
         $unposts = $board_info['unapproved_posts'] ? '<a href="' . $scripturl . '?action=moderate;area=postmod;sa=posts;brd=' . $board . '">' . ($board_info['unapproved_posts'] - $board_info['unapproved_topics']) . '</a>' : 0;
         $context['unapproved_posts_message'] = sprintf($txt['there_are_unapproved_topics'], $untopics, $unposts, $scripturl . '?action=moderate;area=postmod;sa=' . ($board_info['unapproved_topics'] ? 'topics' : 'posts') . ';brd=' . $board);
     }
     // We only know these.
     if (isset($_REQUEST['sort']) && !in_array($_REQUEST['sort'], array('subject', 'starter', 'last_poster', 'replies', 'views', 'likes', 'first_post', 'last_post'))) {
         $_REQUEST['sort'] = 'last_post';
     }
     // Make sure the starting place makes sense and construct the page index.
     if (isset($_REQUEST['sort'])) {
         $sort_string = ';sort=' . $_REQUEST['sort'] . (isset($_REQUEST['desc']) ? ';desc' : '');
     } else {
         $sort_string = '';
     }
     $context['page_index'] = constructPageIndex($scripturl . '?board=' . $board . '.%1$d' . $sort_string, $_REQUEST['start'], $board_info['total_topics'], $maxindex, true);
     $context['start'] =& $_REQUEST['start'];
     // Set a canonical URL for this page.
     $context['canonical_url'] = $scripturl . '?board=' . $board . '.' . $context['start'];
     $context['links'] += array('prev' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?board=' . $board . '.' . ($_REQUEST['start'] - $context['topics_per_page']) : '', 'next' => $_REQUEST['start'] + $context['topics_per_page'] < $board_info['total_topics'] ? $scripturl . '?board=' . $board . '.' . ($_REQUEST['start'] + $context['topics_per_page']) : '');
     $context['page_info'] = array('current_page' => $_REQUEST['start'] / $context['topics_per_page'] + 1, 'num_pages' => floor(($board_info['total_topics'] - 1) / $context['topics_per_page']) + 1);
     if (isset($_REQUEST['all']) && !empty($modSettings['enableAllMessages']) && $maxindex > $modSettings['enableAllMessages']) {
         $maxindex = $modSettings['enableAllMessages'];
         $_REQUEST['start'] = 0;
     }
     // Build a list of the board's moderators.
     $context['moderators'] =& $board_info['moderators'];
     $context['link_moderators'] = array();
     if (!empty($board_info['moderators'])) {
         foreach ($board_info['moderators'] as $mod) {
             $context['link_moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $mod['id'] . '" title="' . $txt['board_moderator'] . '">' . $mod['name'] . '</a>';
         }
     }
     // Mark current and parent boards as seen.
     if (!$user_info['is_guest']) {
         // We can't know they read it if we allow prefetches.
         if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') {
             @ob_end_clean();
             header('HTTP/1.1 403 Prefetch Forbidden');
             die;
         }
         // Mark the board as read, and its parents.
         if (!empty($board_info['parent_boards'])) {
             $board_list = array_keys($board_info['parent_boards']);
             $board_list[] = $board;
         } else {
             $board_list = array($board);
         }
         // Mark boards as read. Boards alone, no need for topics.
         markBoardsRead($board_list, false, false);
         // Clear topicseen cache
         if (!empty($board_info['parent_boards'])) {
             // We've seen all these boards now!
             foreach ($board_info['parent_boards'] as $k => $dummy) {
                 if (isset($_SESSION['topicseen_cache'][$k])) {
                     unset($_SESSION['topicseen_cache'][$k]);
                 }
             }
         }
         if (isset($_SESSION['topicseen_cache'][$board])) {
             unset($_SESSION['topicseen_cache'][$board]);
         }
         // From now on, they've seen it. So we reset notifications.
         $context['is_marked_notify'] = resetSentBoardNotification($user_info['id'], $board);
     } else {
         $context['is_marked_notify'] = false;
     }
     // 'Print' the header and board info.
     $context['page_title'] = strip_tags($board_info['name']);
     // Set the variables up for the template.
     $context['can_mark_notify'] = allowedTo('mark_notify') && !$user_info['is_guest'];
     $context['can_post_new'] = allowedTo('post_new') || $modSettings['postmod_active'] && allowedTo('post_unapproved_topics');
     $context['can_post_poll'] = !empty($modSettings['pollMode']) && allowedTo('poll_post') && $context['can_post_new'];
     $context['can_moderate_forum'] = allowedTo('moderate_forum');
     $context['can_approve_posts'] = allowedTo('approve_posts');
     // Prepare sub-boards for display.
     require_once SUBSDIR . '/BoardsList.class.php';
     $boardIndexOptions = array('include_categories' => false, 'base_level' => $board_info['child_level'] + 1, 'parent_id' => $board_info['id'], 'set_latest_post' => false, 'countChildPosts' => !empty($modSettings['countChildPosts']));
     $boardlist = new Boards_List($boardIndexOptions);
     $context['boards'] = $boardlist->getBoards();
     // Nosey, nosey - who's viewing this board?
     if (!empty($settings['display_who_viewing'])) {
         require_once SUBSDIR . '/Who.subs.php';
         formatViewers($board, 'board');
     }
     // And now, what we're here for: topics!
     require_once SUBSDIR . '/MessageIndex.subs.php';
     // Known sort methods.
     $sort_methods = messageIndexSort();
     // They didn't pick one, default to by last post descending.
     if (!isset($_REQUEST['sort']) || !isset($sort_methods[$_REQUEST['sort']])) {
         $context['sort_by'] = 'last_post';
         $ascending = isset($_REQUEST['asc']);
     } else {
         $context['sort_by'] = $_REQUEST['sort'];
         $ascending = !isset($_REQUEST['desc']);
     }
     $sort_column = $sort_methods[$context['sort_by']];
     $context['sort_direction'] = $ascending ? 'up' : 'down';
     $context['sort_title'] = $ascending ? $txt['sort_desc'] : $txt['sort_asc'];
     // Trick
     $txt['starter'] = $txt['started_by'];
     foreach ($sort_methods as $key => $val) {
         $context['topics_headers'][$key] = array('url' => $scripturl . '?board=' . $context['current_board'] . '.' . $context['start'] . ';sort=' . $key . ($context['sort_by'] == $key && $context['sort_direction'] == 'up' ? ';desc' : ''), 'sort_dir_img' => $context['sort_by'] == $key ? '<img class="sort" src="' . $settings['images_url'] . '/sort_' . $context['sort_direction'] . '.png" alt="" title="' . $context['sort_title'] . '" />' : '');
     }
     // Calculate the fastest way to get the topics.
     $start = (int) $_REQUEST['start'];
     if ($start > ($board_info['total_topics'] - 1) / 2) {
         $ascending = !$ascending;
         $fake_ascending = true;
         $maxindex = $board_info['total_topics'] < $start + $maxindex + 1 ? $board_info['total_topics'] - $start : $maxindex;
         $start = $board_info['total_topics'] < $start + $maxindex + 1 ? 0 : $board_info['total_topics'] - $start - $maxindex;
     } else {
         $fake_ascending = false;
     }
     // Setup the default topic icons...
     $context['icon_sources'] = MessageTopicIcons();
     $topic_ids = array();
     $context['topics'] = array();
     // Set up the query options
     $indexOptions = array('include_sticky' => !empty($modSettings['enableStickyTopics']), 'only_approved' => $modSettings['postmod_active'] && !allowedTo('approve_posts'), 'previews' => !empty($modSettings['message_index_preview']) ? empty($modSettings['preview_characters']) ? -1 : $modSettings['preview_characters'] : 0, 'include_avatars' => !empty($settings['avatars_on_indexes']), 'ascending' => $ascending, 'fake_ascending' => $fake_ascending);
     // Allow integration to modify / add to the $indexOptions
     call_integration_hook('integrate_messageindex_topics', array(&$sort_column, &$indexOptions));
     $topics_info = messageIndexTopics($board, $user_info['id'], $start, $maxindex, $context['sort_by'], $sort_column, $indexOptions);
     // Prepare for links to guests (for search engines)
     $context['pageindex_multiplier'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
     // Begin 'printing' the message index for current board.
     foreach ($topics_info as $row) {
         $topic_ids[] = $row['id_topic'];
         // Do they want message previews?
         if (!empty($modSettings['message_index_preview'])) {
             // Limit them to $modSettings['preview_characters'] characters
             $row['first_body'] = strip_tags(strtr(parse_bbc($row['first_body'], false, $row['id_first_msg']), array('<br />' => "\n", '&nbsp;' => ' ')));
             $row['first_body'] = Util::shorten_text($row['first_body'], !empty($modSettings['preview_characters']) ? $modSettings['preview_characters'] : 128, true);
             // No reply then they are the same, no need to process it again
             if ($row['num_replies'] == 0) {
                 $row['last_body'] == $row['first_body'];
             } else {
                 $row['last_body'] = strip_tags(strtr(parse_bbc($row['last_body'], false, $row['id_last_msg']), array('<br />' => "\n", '&nbsp;' => ' ')));
                 $row['last_body'] = Util::shorten_text($row['last_body'], !empty($modSettings['preview_characters']) ? $modSettings['preview_characters'] : 128, true);
             }
             // Censor the subject and message preview.
             censorText($row['first_subject']);
             censorText($row['first_body']);
             // Don't censor them twice!
             if ($row['id_first_msg'] == $row['id_last_msg']) {
                 $row['last_subject'] = $row['first_subject'];
                 $row['last_body'] = $row['first_body'];
             } else {
                 censorText($row['last_subject']);
                 censorText($row['last_body']);
             }
         } else {
             $row['first_body'] = '';
             $row['last_body'] = '';
             censorText($row['first_subject']);
             if ($row['id_first_msg'] == $row['id_last_msg']) {
                 $row['last_subject'] = $row['first_subject'];
             } else {
                 censorText($row['last_subject']);
             }
         }
         // Decide how many pages the topic should have.
         if ($row['num_replies'] + 1 > $context['messages_per_page']) {
             // We can't pass start by reference.
             $start = -1;
             $pages = constructPageIndex($scripturl . '?topic=' . $row['id_topic'] . '.%1$d', $start, $row['num_replies'] + 1, $context['messages_per_page'], true, array('prev_next' => false, 'all' => !empty($modSettings['enableAllMessages']) && $row['num_replies'] + 1 < $modSettings['enableAllMessages']));
         } else {
             $pages = '';
         }
         // We need to check the topic icons exist...
         if (!empty($modSettings['messageIconChecks_enable'])) {
             if (!isset($context['icon_sources'][$row['first_icon']])) {
                 $context['icon_sources'][$row['first_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['first_icon'] . '.png') ? 'images_url' : 'default_images_url';
             }
             if (!isset($context['icon_sources'][$row['last_icon']])) {
                 $context['icon_sources'][$row['last_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['last_icon'] . '.png') ? 'images_url' : 'default_images_url';
             }
         } else {
             if (!isset($context['icon_sources'][$row['first_icon']])) {
                 $context['icon_sources'][$row['first_icon']] = 'images_url';
             }
             if (!isset($context['icon_sources'][$row['last_icon']])) {
                 $context['icon_sources'][$row['last_icon']] = 'images_url';
             }
         }
         // 'Print' the topic info.
         $context['topics'][$row['id_topic']] = array('id' => $row['id_topic'], 'first_post' => array('id' => $row['id_first_msg'], 'member' => array('username' => $row['first_member_name'], 'name' => $row['first_display_name'], 'id' => $row['first_id_member'], 'href' => !empty($row['first_id_member']) ? $scripturl . '?action=profile;u=' . $row['first_id_member'] : '', 'link' => !empty($row['first_id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['first_id_member'] . '" title="' . $txt['profile_of'] . ' ' . $row['first_display_name'] . '" class="preview">' . $row['first_display_name'] . '</a>' : $row['first_display_name']), 'time' => standardTime($row['first_poster_time']), 'html_time' => htmlTime($row['first_poster_time']), 'timestamp' => forum_time(true, $row['first_poster_time']), 'subject' => $row['first_subject'], 'preview' => trim($row['first_body']), 'icon' => $row['first_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.png', 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0', 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0">' . $row['first_subject'] . '</a>'), 'last_post' => array('id' => $row['id_last_msg'], 'member' => array('username' => $row['last_member_name'], 'name' => $row['last_display_name'], 'id' => $row['last_id_member'], 'href' => !empty($row['last_id_member']) ? $scripturl . '?action=profile;u=' . $row['last_id_member'] : '', 'link' => !empty($row['last_id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['last_id_member'] . '">' . $row['last_display_name'] . '</a>' : $row['last_display_name']), 'time' => standardTime($row['last_poster_time']), 'html_time' => htmlTime($row['last_poster_time']), 'timestamp' => forum_time(true, $row['last_poster_time']), 'subject' => $row['last_subject'], 'preview' => trim($row['last_body']), 'icon' => $row['last_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['last_icon']]] . '/post/' . $row['last_icon'] . '.png', 'href' => $scripturl . '?topic=' . $row['id_topic'] . ($user_info['is_guest'] ? '.' . (int) ($row['num_replies'] / $context['pageindex_multiplier']) * $context['pageindex_multiplier'] . '#msg' . $row['id_last_msg'] : ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['id_last_msg']) . '#new'), 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . ($user_info['is_guest'] ? '.' . (int) ($row['num_replies'] / $context['pageindex_multiplier']) * $context['pageindex_multiplier'] . '#msg' . $row['id_last_msg'] : ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['id_last_msg']) . '#new') . '" ' . ($row['num_replies'] == 0 ? '' : 'rel="nofollow"') . '>' . $row['last_subject'] . '</a>'), 'default_preview' => trim($row[!empty($modSettings['message_index_preview']) && $modSettings['message_index_preview'] == 2 ? 'last_body' : 'first_body']), 'is_sticky' => !empty($modSettings['enableStickyTopics']) && !empty($row['is_sticky']), 'is_locked' => !empty($row['locked']), 'is_poll' => !empty($modSettings['pollMode']) && $row['id_poll'] > 0, 'is_hot' => !empty($modSettings['useLikesNotViews']) ? $row['num_likes'] >= $modSettings['hotTopicPosts'] : $row['num_replies'] >= $modSettings['hotTopicPosts'], 'is_very_hot' => !empty($modSettings['useLikesNotViews']) ? $row['num_likes'] >= $modSettings['hotTopicVeryPosts'] : $row['num_replies'] >= $modSettings['hotTopicVeryPosts'], 'is_posted_in' => false, 'icon' => $row['first_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.png', 'subject' => $row['first_subject'], 'new' => $row['new_from'] <= $row['id_msg_modified'], 'new_from' => $row['new_from'], 'newtime' => $row['new_from'], 'new_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['new_from'] . '#new', 'redir_href' => !empty($row['id_redirect_topic']) ? $scripturl . '?topic=' . $row['id_topic'] . '.0;noredir' : '', 'pages' => $pages, 'replies' => comma_format($row['num_replies']), 'views' => comma_format($row['num_views']), 'likes' => comma_format($row['num_likes']), 'approved' => $row['approved'], 'unapproved_posts' => $row['unapproved_posts']);
         if (!empty($settings['avatars_on_indexes'])) {
             $context['topics'][$row['id_topic']]['last_post']['member']['avatar'] = determineAvatar($row);
         }
         determineTopicClass($context['topics'][$row['id_topic']]);
     }
     // Allow addons to add to the $context['topics']
     call_integration_hook('integrate_messageindex_listing', array($topics_info));
     // Fix the sequence of topics if they were retrieved in the wrong order. (for speed reasons...)
     if ($fake_ascending) {
         $context['topics'] = array_reverse($context['topics'], true);
     }
     if (!empty($modSettings['enableParticipation']) && !$user_info['is_guest'] && !empty($topic_ids)) {
         $topics_participated_in = topicsParticipation($user_info['id'], $topic_ids);
         foreach ($topics_participated_in as $participated) {
             $context['topics'][$participated['id_topic']]['is_posted_in'] = true;
             $context['topics'][$participated['id_topic']]['class'] = 'my_' . $context['topics'][$participated['id_topic']]['class'];
         }
     }
     $context['jump_to'] = array('label' => addslashes(un_htmlspecialchars($txt['jump_to'])), 'board_name' => htmlspecialchars(strtr(strip_tags($board_info['name']), array('&amp;' => '&')), ENT_COMPAT, 'UTF-8'), 'child_level' => $board_info['child_level']);
     // Is Quick Moderation active/needed?
     if (!empty($options['display_quick_mod']) && !empty($context['topics'])) {
         $context['can_markread'] = $context['user']['is_logged'];
         $context['can_lock'] = allowedTo('lock_any');
         $context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']);
         $context['can_move'] = allowedTo('move_any');
         $context['can_remove'] = allowedTo('remove_any');
         $context['can_merge'] = allowedTo('merge_any');
         // Ignore approving own topics as it's unlikely to come up...
         $context['can_approve'] = $modSettings['postmod_active'] && allowedTo('approve_posts') && !empty($board_info['unapproved_topics']);
         // Can we restore topics?
         $context['can_restore'] = allowedTo('move_any') && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board;
         // Set permissions for all the topics.
         foreach ($context['topics'] as $t => $topic) {
             $started = $topic['first_post']['member']['id'] == $user_info['id'];
             $context['topics'][$t]['quick_mod'] = array('lock' => allowedTo('lock_any') || $started && allowedTo('lock_own'), 'sticky' => allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']), 'move' => allowedTo('move_any') || $started && allowedTo('move_own'), 'modify' => allowedTo('modify_any') || $started && allowedTo('modify_own'), 'remove' => allowedTo('remove_any') || $started && allowedTo('remove_own'), 'approve' => $context['can_approve'] && $topic['unapproved_posts']);
             $context['can_lock'] |= $started && allowedTo('lock_own');
             $context['can_move'] |= $started && allowedTo('move_own');
             $context['can_remove'] |= $started && allowedTo('remove_own');
         }
         // Can we use quick moderation checkboxes?
         if ($options['display_quick_mod'] == 1) {
             $context['can_quick_mod'] = $context['user']['is_logged'] || $context['can_approve'] || $context['can_remove'] || $context['can_lock'] || $context['can_sticky'] || $context['can_move'] || $context['can_merge'] || $context['can_restore'];
         } else {
             $context['can_quick_mod'] = $context['can_remove'] || $context['can_lock'] || $context['can_sticky'] || $context['can_move'];
         }
     }
     if (!empty($context['can_quick_mod']) && $options['display_quick_mod'] == 1) {
         $context['qmod_actions'] = array('approve', 'remove', 'lock', 'sticky', 'move', 'merge', 'restore', 'markread');
         call_integration_hook('integrate_quick_mod_actions');
     }
     if (!empty($context['boards']) && $context['start'] == 0) {
         $template_layers->add('display_child_boards');
     }
     // If there are children, but no topics and no ability to post topics...
     $context['no_topic_listing'] = !empty($context['boards']) && empty($context['topics']) && !$context['can_post_new'];
     $template_layers->add('topic_listing');
     addJavascriptVar(array('notification_board_notice' => $context['is_marked_notify'] ? $txt['notification_disable_board'] : $txt['notification_enable_board']), true);
     // Build the message index button array.
     $context['normal_buttons'] = array('new_topic' => array('test' => 'can_post_new', 'text' => 'new_topic', 'image' => 'new_topic.png', 'lang' => true, 'url' => $scripturl . '?action=post;board=' . $context['current_board'] . '.0', 'active' => true), 'notify' => array('test' => 'can_mark_notify', 'text' => $context['is_marked_notify'] ? 'unnotify' : 'notify', 'image' => ($context['is_marked_notify'] ? 'un' : '') . 'notify.png', 'lang' => true, 'custom' => 'onclick="return notifyboardButton(this);"', 'url' => $scripturl . '?action=notifyboard;sa=' . ($context['is_marked_notify'] ? 'off' : 'on') . ';board=' . $context['current_board'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']));
     // They can only mark read if they are logged in and it's enabled!
     if (!$user_info['is_guest'] && $settings['show_mark_read']) {
         $context['normal_buttons']['markread'] = array('text' => 'mark_read_short', 'image' => 'markread.png', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=board;board=' . $context['current_board'] . '.0;' . $context['session_var'] . '=' . $context['session_id'], 'custom' => 'onclick="return markboardreadButton(this);"');
     }
     // Allow adding new buttons easily.
     call_integration_hook('integrate_messageindex_buttons');
 }
function method_mark_all_as_read()
{
    global $mobdb, $context, $scripturl, $user_info, $modSettings, $sourcedir;
    // Guest?
    if ($user_info['is_guest']) {
        createErrorResponse(8);
    }
    $whereadd = '';
    if (isset($context['mob_request']['params'][0][0])) {
        $id_board = intval($context['mob_request']['params'][0][0]);
        $whereadd = " AND b.ID_BOARD={$id_board}";
    }
    // Get all the boards this user can see
    $mobdb->query('
        SELECT b.ID_BOARD AS id_board
        FROM {db_prefix}boards AS b
        WHERE {query_see_board}' . $whereadd, array());
    $boards = array();
    while ($row = $mobdb->fetch_assoc()) {
        $boards[] = $row['id_board'];
    }
    $mobdb->free_result();
    // We got boards?
    if (!empty($boards)) {
        require_once $sourcedir . '/Subs-Boards.php';
        markBoardsRead($boards, false);
    }
    outputRPCResult(true);
}
Beispiel #11
0
    /**
     * Find unread topics and replies.
     * Accessed by action=unread and action=unreadreplies
     */
    public function action_unread()
    {
        global $board, $txt, $scripturl;
        global $user_info, $context, $settings, $modSettings, $options;
        $db = database();
        // Guests can't have unread things, we don't know anything about them.
        is_not_guest();
        // Prefetching + lots of MySQL work = bad mojo.
        if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') {
            @ob_end_clean();
            header('HTTP/1.1 403 Forbidden');
            die;
        }
        // We need... we need... I know!
        require_once SUBSDIR . '/Recent.subs.php';
        require_once SUBSDIR . '/Boards.subs.php';
        $context['showCheckboxes'] = !empty($options['display_quick_mod']) && $options['display_quick_mod'] == 1 && $settings['show_mark_read'];
        $context['showing_all_topics'] = isset($_GET['all']);
        $context['start'] = (int) $_REQUEST['start'];
        $context['topics_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page']) ? $options['topics_per_page'] : $modSettings['defaultMaxTopics'];
        if ($_REQUEST['action'] == 'unread') {
            $context['page_title'] = $context['showing_all_topics'] ? $txt['unread_topics_all'] : $txt['unread_topics_visit'];
        } else {
            $context['page_title'] = $txt['unread_replies'];
        }
        if ($context['showing_all_topics'] && !empty($modSettings['loadavg_allunread']) && $modSettings['current_load'] >= $modSettings['loadavg_allunread']) {
            fatal_lang_error('loadavg_allunread_disabled', false);
        } elseif ($_REQUEST['action'] != 'unread' && !empty($modSettings['loadavg_unreadreplies']) && $modSettings['current_load'] >= $modSettings['loadavg_unreadreplies']) {
            fatal_lang_error('loadavg_unreadreplies_disabled', false);
        } elseif (!$context['showing_all_topics'] && $_REQUEST['action'] == 'unread' && !empty($modSettings['loadavg_unread']) && $modSettings['current_load'] >= $modSettings['loadavg_unread']) {
            fatal_lang_error('loadavg_unread_disabled', false);
        }
        // Parameters for the main query.
        $query_parameters = array();
        // Are we specifying any specific board?
        if (isset($_REQUEST['children']) && (!empty($board) || !empty($_REQUEST['boards']))) {
            $boards = array();
            if (!empty($_REQUEST['boards'])) {
                $_REQUEST['boards'] = explode(',', $_REQUEST['boards']);
                foreach ($_REQUEST['boards'] as $b) {
                    $boards[] = (int) $b;
                }
            }
            if (!empty($board)) {
                $boards[] = (int) $board;
            }
            // The easiest thing is to just get all the boards they can see,
            // but since we've specified the top of tree we ignore some of them
            addChildBoards($boards);
            if (empty($boards)) {
                fatal_lang_error('error_no_boards_selected');
            }
            $query_this_board = 'id_board IN ({array_int:boards})';
            $query_parameters['boards'] = $boards;
            $context['querystring_board_limits'] = ';boards=' . implode(',', $boards) . ';start=%d';
        } elseif (!empty($board)) {
            $query_this_board = 'id_board = {int:board}';
            $query_parameters['board'] = $board;
            $context['querystring_board_limits'] = ';board=' . $board . '.%1$d';
        } elseif (!empty($_REQUEST['boards'])) {
            $selected_boards = array_map('intval', explode(',', $_REQUEST['boards']));
            $boards = accessibleBoards($selected_boards);
            if (empty($boards)) {
                fatal_lang_error('error_no_boards_selected');
            }
            $query_this_board = 'id_board IN ({array_int:boards})';
            $query_parameters['boards'] = $boards;
            $context['querystring_board_limits'] = ';boards=' . implode(',', $boards) . ';start=%1$d';
        } elseif (!empty($_REQUEST['c'])) {
            $categories = array_map('intval', explode(',', $_REQUEST['c']));
            $boards = array_keys(boardsPosts(array(), $categories, isset($_REQUEST['action']) && $_REQUEST['action'] != 'unreadreplies'));
            if (empty($boards)) {
                fatal_lang_error('error_no_boards_selected');
            }
            $query_this_board = 'id_board IN ({array_int:boards})';
            $query_parameters['boards'] = $boards;
            $context['querystring_board_limits'] = ';c=' . $_REQUEST['c'] . ';start=%1$d';
        } else {
            $see_board = isset($_REQUEST['action']) && $_REQUEST['action'] == 'unreadreplies' ? 'query_see_board' : 'query_wanna_see_board';
            // Don't bother to show deleted posts!
            $boards = wantedBoards($see_board);
            if (empty($boards)) {
                fatal_lang_error('error_no_boards_selected');
            }
            $query_this_board = 'id_board IN ({array_int:boards})';
            $query_parameters['boards'] = $boards;
            $context['querystring_board_limits'] = ';start=%1$d';
            $context['no_board_limits'] = true;
        }
        $sort_methods = array('subject' => 'ms.subject', 'starter' => 'IFNULL(mems.real_name, ms.poster_name)', 'replies' => 't.num_replies', 'views' => 't.num_views', 'first_post' => 't.id_topic', 'last_post' => 't.id_last_msg');
        // The default is the most logical: newest first.
        if (!isset($_REQUEST['sort']) || !isset($sort_methods[$_REQUEST['sort']])) {
            $context['sort_by'] = 'last_post';
            $ascending = isset($_REQUEST['asc']);
            $context['querystring_sort_limits'] = $ascending ? ';asc' : '';
        } else {
            $context['sort_by'] = $_REQUEST['sort'];
            $ascending = !isset($_REQUEST['desc']);
            $context['querystring_sort_limits'] = ';sort=' . $context['sort_by'] . ($ascending ? '' : ';desc');
        }
        $_REQUEST['sort'] = $sort_methods[$context['sort_by']];
        $context['sort_direction'] = $ascending ? 'up' : 'down';
        $context['sort_title'] = $ascending ? $txt['sort_desc'] : $txt['sort_asc'];
        // Trick
        $txt['starter'] = $txt['started_by'];
        foreach ($sort_methods as $key => $val) {
            $context['topics_headers'][$key] = array('url' => $scripturl . '?action=unread' . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start']) . ';sort=subject' . ($context['sort_by'] == 'subject' && $context['sort_direction'] == 'up' ? ';desc' : ''), 'sort_dir_img' => $context['sort_by'] == $key ? '<img class="sort" src="' . $settings['images_url'] . '/sort_' . $context['sort_direction'] . '.png" alt="" title="' . $context['sort_title'] . '" />' : '');
        }
        if (!empty($_REQUEST['c']) && is_array($_REQUEST['c']) && count($_REQUEST['c']) == 1) {
            $name = categoryName((int) $_REQUEST['c'][0]);
            $context['linktree'][] = array('url' => $scripturl . '#c' . (int) $_REQUEST['c'][0], 'name' => $name);
        }
        $context['linktree'][] = array('url' => $scripturl . '?action=' . $_REQUEST['action'] . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'], 'name' => $_REQUEST['action'] == 'unread' ? $txt['unread_topics_visit'] : $txt['unread_replies']);
        if ($context['showing_all_topics']) {
            $context['linktree'][] = array('url' => $scripturl . '?action=' . $_REQUEST['action'] . ';all' . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'], 'name' => $txt['unread_topics_all']);
            $txt['unread_topics_visit_none'] = str_replace('{unread_all_url}', $scripturl . '?action=unread;all', $txt['unread_topics_visit_none']);
        } else {
            $txt['unread_topics_visit_none'] = str_replace('{unread_all_url}', $scripturl . '?action=unread;all' . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'], $txt['unread_topics_visit_none']);
        }
        loadTemplate('Recent');
        $context['sub_template'] = $_REQUEST['action'] == 'unread' ? 'unread' : 'replies';
        // Setup the default topic icons... for checking they exist and the like ;)
        require_once SUBSDIR . '/MessageIndex.subs.php';
        $context['icon_sources'] = MessageTopicIcons();
        $is_topics = $_REQUEST['action'] == 'unread';
        // If empty, no preview at all
        if (empty($modSettings['message_index_preview'])) {
            $preview_bodies = '';
        } elseif (empty($modSettings['preview_characters'])) {
            $preview_bodies = 'ml.body AS last_body, ms.body AS first_body,';
        } else {
            $preview_bodies = 'SUBSTRING(ml.body, 1, ' . ($modSettings['preview_characters'] + 256) . ') AS last_body, SUBSTRING(ms.body, 1, ' . ($modSettings['preview_characters'] + 256) . ') AS first_body,';
        }
        // This part is the same for each query.
        $select_clause = '
					ms.subject AS first_subject, ms.poster_time AS first_poster_time, ms.id_topic, t.id_board, b.name AS bname,
					t.num_replies, t.num_views, t.num_likes, ms.id_member AS id_first_member, ml.id_member AS id_last_member,
					ml.poster_time AS last_poster_time, IFNULL(mems.real_name, ms.poster_name) AS first_poster_name,
					IFNULL(meml.real_name, ml.poster_name) AS last_poster_name, ml.subject AS last_subject,
					ml.icon AS last_icon, ms.icon AS first_icon, t.id_poll, t.is_sticky, t.locked, ml.modified_time AS last_modified_time,
					IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from,
					' . $preview_bodies . '
					ml.smileys_enabled AS last_smileys, ms.smileys_enabled AS first_smileys, t.id_first_msg, t.id_last_msg';
        if ($context['showing_all_topics']) {
            $earliest_msg = earliest_msg();
        }
        // @todo Add modified_time in for log_time check?
        if ($modSettings['totalMessages'] > 100000 && $context['showing_all_topics']) {
            $db->query('', '
				DROP TABLE IF EXISTS {db_prefix}log_topics_unread', array());
            // Let's copy things out of the log_topics table, to reduce searching.
            $have_temp_table = $db->query('', '
				CREATE TEMPORARY TABLE {db_prefix}log_topics_unread (
					PRIMARY KEY (id_topic)
				)
				SELECT lt.id_topic, lt.id_msg, lt.unwatched
				FROM {db_prefix}topics AS t
					INNER JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic)
				WHERE lt.id_member = {int:current_member}
					AND t.' . $query_this_board . (empty($earliest_msg) ? '' : '
					AND t.id_last_msg > {int:earliest_msg}') . ($modSettings['postmod_active'] ? '
					AND t.approved = {int:is_approved}' : '') . ($modSettings['enable_unwatch'] ? '
					AND lt.unwatched != 1' : ''), array_merge($query_parameters, array('current_member' => $user_info['id'], 'earliest_msg' => !empty($earliest_msg) ? $earliest_msg : 0, 'is_approved' => 1, 'db_error_skip' => true))) !== false;
        } else {
            $have_temp_table = false;
        }
        if ($context['showing_all_topics'] && $have_temp_table) {
            $request = $db->query('', '
				SELECT COUNT(*), MIN(t.id_last_msg)
				FROM {db_prefix}topics AS t
					LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic)
					LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
				WHERE t.' . $query_this_board . (!empty($earliest_msg) ? '
					AND t.id_last_msg > {int:earliest_msg}' : '') . '
					AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '') . ($modSettings['enable_unwatch'] ? ' AND IFNULL(lt.unwatched, 0) != 1' : ''), array_merge($query_parameters, array('current_member' => $user_info['id'], 'earliest_msg' => !empty($earliest_msg) ? $earliest_msg : 0, 'is_approved' => 1)));
            list($num_topics, $min_message) = $db->fetch_row($request);
            $db->free_result($request);
            // Make sure the starting place makes sense and construct the page index.
            $context['page_index'] = constructPageIndex($scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . $context['querystring_board_limits'] . $context['querystring_sort_limits'], $_REQUEST['start'], $num_topics, $context['topics_per_page'], true);
            $context['current_page'] = (int) $_REQUEST['start'] / $context['topics_per_page'];
            $context['links'] += array('prev' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] - $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'next' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] + $context['topics_per_page']) . $context['querystring_sort_limits'] : '');
            $context['page_info'] = array('current_page' => $_REQUEST['start'] / $context['topics_per_page'] + 1, 'num_pages' => floor(($num_topics - 1) / $context['topics_per_page']) + 1);
            if ($num_topics == 0) {
                // Mark the boards as read if there are no unread topics!
                // @todo look at this... there are no more unread topics already.
                // If clearing of log_topics is still needed, perhaps do it separately.
                markBoardsRead(empty($boards) ? $board : $boards, false, true);
                $context['topics'] = array();
                if ($context['querystring_board_limits'] == ';start=%1$d') {
                    $context['querystring_board_limits'] = '';
                } else {
                    $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']);
                }
                return;
            } else {
                $min_message = (int) $min_message;
            }
            $request = $db->query('substring', '
				SELECT ' . $select_clause . '
				FROM {db_prefix}messages AS ms
					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ms.id_topic AND t.id_first_msg = ms.id_msg)
					INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
					LEFT JOIN {db_prefix}boards AS b ON (b.id_board = ms.id_board)
					LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)
					LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)
					LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic)
					LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
				WHERE b.' . $query_this_board . '
					AND t.id_last_msg >= {int:min_message}
					AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? ' AND ms.approved = {int:is_approved}' : '') . ($modSettings['enable_unwatch'] ? ' AND IFNULL(lt.unwatched, 0) != 1' : '') . '
				ORDER BY {raw:sort}
				LIMIT {int:offset}, {int:limit}', array_merge($query_parameters, array('current_member' => $user_info['id'], 'min_message' => $min_message, 'is_approved' => 1, 'sort' => $_REQUEST['sort'] . ($ascending ? '' : ' DESC'), 'offset' => $_REQUEST['start'], 'limit' => $context['topics_per_page'])));
        } elseif ($is_topics) {
            $request = $db->query('', '
				SELECT COUNT(*), MIN(t.id_last_msg)
				FROM {db_prefix}topics AS t' . (!empty($have_temp_table) ? '
					LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic)' : '
					LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})') . '
					LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
				WHERE t.' . $query_this_board . ($context['showing_all_topics'] && !empty($earliest_msg) ? '
					AND t.id_last_msg > {int:earliest_msg}' : (!$context['showing_all_topics'] && empty($_SESSION['first_login']) ? '
					AND t.id_last_msg > {int:id_msg_last_visit}' : '')) . '
					AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '') . ($modSettings['enable_unwatch'] ? ' AND IFNULL(lt.unwatched, 0) != 1' : ''), array_merge($query_parameters, array('current_member' => $user_info['id'], 'earliest_msg' => !empty($earliest_msg) ? $earliest_msg : 0, 'id_msg_last_visit' => $_SESSION['id_msg_last_visit'], 'is_approved' => 1)));
            list($num_topics, $min_message) = $db->fetch_row($request);
            $db->free_result($request);
            // Make sure the starting place makes sense and construct the page index.
            $context['page_index'] = constructPageIndex($scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . $context['querystring_board_limits'] . $context['querystring_sort_limits'], $_REQUEST['start'], $num_topics, $context['topics_per_page'], true);
            $context['current_page'] = (int) $_REQUEST['start'] / $context['topics_per_page'];
            $context['links'] += array('prev' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] - $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'next' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] + $context['topics_per_page']) . $context['querystring_sort_limits'] : '');
            $context['page_info'] = array('current_page' => $_REQUEST['start'] / $context['topics_per_page'] + 1, 'num_pages' => floor(($num_topics - 1) / $context['topics_per_page']) + 1);
            if ($num_topics == 0) {
                // Is this an all topics query?
                if ($context['showing_all_topics']) {
                    // Since there are no unread topics, mark the boards as read!
                    // @todo look at this... there are no more unread topics already.
                    // If clearing of log_topics is still needed, perhaps do it separately.
                    markBoardsRead(empty($boards) ? $board : $boards, false, true);
                }
                $context['topics'] = array();
                if ($context['querystring_board_limits'] == ';start=%d') {
                    $context['querystring_board_limits'] = '';
                } else {
                    $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']);
                }
                return;
            } else {
                $min_message = (int) $min_message;
            }
            $request = $db->query('substring', '
				SELECT ' . $select_clause . '
				FROM {db_prefix}messages AS ms
					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = ms.id_topic AND t.id_first_msg = ms.id_msg)
					INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
					LEFT JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
					LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)
					LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)' . (!empty($have_temp_table) ? '
					LEFT JOIN {db_prefix}log_topics_unread AS lt ON (lt.id_topic = t.id_topic)' : '
					LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})') . '
					LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
				WHERE t.' . $query_this_board . '
					AND t.id_last_msg >= {int:min_message}
					AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < ml.id_msg' . ($modSettings['postmod_active'] ? ' AND ms.approved = {int:is_approved}' : '') . ($modSettings['enable_unwatch'] ? ' AND IFNULL(lt.unwatched, 0) != 1' : '') . '
				ORDER BY {raw:order}
				LIMIT {int:offset}, {int:limit}', array_merge($query_parameters, array('current_member' => $user_info['id'], 'min_message' => $min_message, 'is_approved' => 1, 'order' => $_REQUEST['sort'] . ($ascending ? '' : ' DESC'), 'offset' => $_REQUEST['start'], 'limit' => $context['topics_per_page'])));
        } else {
            if ($modSettings['totalMessages'] > 100000) {
                $db->query('', '
					DROP TABLE IF EXISTS {db_prefix}topics_posted_in', array());
                $db->query('', '
					DROP TABLE IF EXISTS {db_prefix}log_topics_posted_in', array());
                $sortKey_joins = array('ms.subject' => '
						INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)', 'IFNULL(mems.real_name, ms.poster_name)' => '
						INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)
						LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)');
                // The main benefit of this temporary table is not that it's faster; it's that it avoids locks later.
                $have_temp_table = $db->query('', '
					CREATE TEMPORARY TABLE {db_prefix}topics_posted_in (
						id_topic mediumint(8) unsigned NOT NULL default {string:string_zero},
						id_board smallint(5) unsigned NOT NULL default {string:string_zero},
						id_last_msg int(10) unsigned NOT NULL default {string:string_zero},
						id_msg int(10) unsigned NOT NULL default {string:string_zero},
						PRIMARY KEY (id_topic)
					)
					SELECT t.id_topic, t.id_board, t.id_last_msg, IFNULL(lmr.id_msg, 0) AS id_msg' . (!in_array($_REQUEST['sort'], array('t.id_last_msg', 't.id_topic')) ? ', ' . $_REQUEST['sort'] . ' AS sort_key' : '') . '
					FROM {db_prefix}messages AS m
						INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)' . ($modSettings['enable_unwatch'] ? '
						LEFT JOIN {db_prefix}log_topics AS lt ON (t.id_topic = lt.id_topic AND lt.id_member = {int:current_member})' : '') . '
						LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})' . (isset($sortKey_joins[$_REQUEST['sort']]) ? $sortKey_joins[$_REQUEST['sort']] : '') . '
					WHERE m.id_member = {int:current_member}' . (!empty($board) ? '
						AND t.id_board = {int:current_board}' : '') . ($modSettings['postmod_active'] ? '
						AND t.approved = {int:is_approved}' : '') . ($modSettings['enable_unwatch'] ? '
						AND IFNULL(lt.unwatched, 0) != 1' : '') . '
					GROUP BY m.id_topic', array('current_board' => $board, 'current_member' => $user_info['id'], 'is_approved' => 1, 'string_zero' => '0', 'db_error_skip' => true)) !== false;
                // If that worked, create a sample of the log_topics table too.
                if ($have_temp_table) {
                    $have_temp_table = $db->query('', '
						CREATE TEMPORARY TABLE {db_prefix}log_topics_posted_in (
							PRIMARY KEY (id_topic)
						)
						SELECT lt.id_topic, lt.id_msg
						FROM {db_prefix}log_topics AS lt
							INNER JOIN {db_prefix}topics_posted_in AS pi ON (pi.id_topic = lt.id_topic)
						WHERE lt.id_member = {int:current_member}', array('current_member' => $user_info['id'], 'db_error_skip' => true)) !== false;
                }
            }
            if (!empty($have_temp_table)) {
                $request = $db->query('', '
					SELECT COUNT(*)
					FROM {db_prefix}topics_posted_in AS pi
						LEFT JOIN {db_prefix}log_topics_posted_in AS lt ON (lt.id_topic = pi.id_topic)
					WHERE pi.' . $query_this_board . '
						AND IFNULL(lt.id_msg, pi.id_msg) < pi.id_last_msg', array_merge($query_parameters, array()));
                list($num_topics) = $db->fetch_row($request);
                $db->free_result($request);
                $min_message = 0;
            } else {
                $request = $db->query('unread_fetch_topic_count', '
					SELECT COUNT(DISTINCT t.id_topic), MIN(t.id_last_msg)
					FROM {db_prefix}topics AS t
						INNER JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic)
						LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
						LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
					WHERE t.' . $query_this_board . '
						AND m.id_member = {int:current_member}
						AND IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0)) < t.id_last_msg' . ($modSettings['postmod_active'] ? '
						AND t.approved = {int:is_approved}' : '') . ($modSettings['enable_unwatch'] ? '
						AND IFNULL(lt.unwatched, 0) != 1' : ''), array_merge($query_parameters, array('current_member' => $user_info['id'], 'is_approved' => 1)));
                list($num_topics, $min_message) = $db->fetch_row($request);
                $db->free_result($request);
            }
            // Make sure the starting place makes sense and construct the page index.
            $context['page_index'] = constructPageIndex($scripturl . '?action=' . $_REQUEST['action'] . $context['querystring_board_limits'] . $context['querystring_sort_limits'], $_REQUEST['start'], $num_topics, $context['topics_per_page'], true);
            $context['current_page'] = (int) $_REQUEST['start'] / $context['topics_per_page'];
            $context['links'] += array('first' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], 0) . $context['querystring_sort_limits'] : '', 'prev' => $_REQUEST['start'] >= $context['topics_per_page'] ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] - $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'next' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], $_REQUEST['start'] + $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'last' => $_REQUEST['start'] + $context['topics_per_page'] < $num_topics ? $scripturl . '?action=' . $_REQUEST['action'] . ($context['showing_all_topics'] ? ';all' : '') . sprintf($context['querystring_board_limits'], floor(($num_topics - 1) / $context['topics_per_page']) * $context['topics_per_page']) . $context['querystring_sort_limits'] : '', 'up' => $scripturl);
            $context['page_info'] = array('current_page' => $_REQUEST['start'] / $context['topics_per_page'] + 1, 'num_pages' => floor(($num_topics - 1) / $context['topics_per_page']) + 1);
            if ($num_topics == 0) {
                $context['topics'] = array();
                if ($context['querystring_board_limits'] == ';start=%d') {
                    $context['querystring_board_limits'] = '';
                } else {
                    $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']);
                }
                return;
            }
            if (!empty($have_temp_table)) {
                $request = $db->query('', '
					SELECT t.id_topic
					FROM {db_prefix}topics_posted_in AS t
						LEFT JOIN {db_prefix}log_topics_posted_in AS lt ON (lt.id_topic = t.id_topic)
					WHERE t.' . $query_this_board . '
						AND IFNULL(lt.id_msg, t.id_msg) < t.id_last_msg
					ORDER BY {raw:order}
					LIMIT {int:offset}, {int:limit}', array_merge($query_parameters, array('order' => (in_array($_REQUEST['sort'], array('t.id_last_msg', 't.id_topic')) ? $_REQUEST['sort'] : 't.sort_key') . ($ascending ? '' : ' DESC'), 'offset' => $_REQUEST['start'], 'limit' => $context['topics_per_page'])));
            } else {
                $request = $db->query('unread_replies', '
					SELECT DISTINCT t.id_topic
					FROM {db_prefix}topics AS t
						INNER JOIN {db_prefix}messages AS m ON (m.id_topic = t.id_topic AND m.id_member = {int:current_member})' . (strpos($_REQUEST['sort'], 'ms.') === false ? '' : '
						INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)') . (strpos($_REQUEST['sort'], 'mems.') === false ? '' : '
						LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)') . '
						LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
						LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
					WHERE t.' . $query_this_board . '
						AND t.id_last_msg >= {int:min_message}
						AND (IFNULL(lt.id_msg, IFNULL(lmr.id_msg, 0))) < t.id_last_msg' . ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '') . ($modSettings['enable_unwatch'] ? ' AND IFNULL(lt.unwatched, 0) != 1' : '') . '
					ORDER BY {raw:order}
					LIMIT {int:offset}, {int:limit}', array_merge($query_parameters, array('current_member' => $user_info['id'], 'min_message' => (int) $min_message, 'is_approved' => 1, 'order' => $_REQUEST['sort'] . ($ascending ? '' : ' DESC'), 'offset' => $_REQUEST['start'], 'limit' => $context['topics_per_page'], 'sort' => $_REQUEST['sort'])));
            }
            $topics = array();
            while ($row = $db->fetch_assoc($request)) {
                $topics[] = $row['id_topic'];
            }
            $db->free_result($request);
            // Sanity... where have you gone?
            if (empty($topics)) {
                $context['topics'] = array();
                if ($context['querystring_board_limits'] == ';start=%d') {
                    $context['querystring_board_limits'] = '';
                } else {
                    $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']);
                }
                return;
            }
            $request = $db->query('substring', '
				SELECT ' . $select_clause . '
				FROM {db_prefix}topics AS t
					INNER JOIN {db_prefix}messages AS ms ON (ms.id_topic = t.id_topic AND ms.id_msg = t.id_first_msg)
					INNER JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
					INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
					LEFT JOIN {db_prefix}members AS mems ON (mems.id_member = ms.id_member)
					LEFT JOIN {db_prefix}members AS meml ON (meml.id_member = ml.id_member)
					LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
					LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = t.id_board AND lmr.id_member = {int:current_member})
				WHERE t.id_topic IN ({array_int:topic_list})
				ORDER BY ' . $_REQUEST['sort'] . ($ascending ? '' : ' DESC') . '
				LIMIT ' . count($topics), array('current_member' => $user_info['id'], 'topic_list' => $topics));
        }
        $context['topics'] = array();
        $topic_ids = array();
        while ($row = $db->fetch_assoc($request)) {
            $topic_ids[] = $row['id_topic'];
            if (!empty($modSettings['message_index_preview'])) {
                // Limit them to 128 characters - do this FIRST because it's a lot of wasted censoring otherwise.
                $row['first_body'] = strip_tags(strtr(parse_bbc($row['first_body'], false, $row['id_first_msg']), array('<br />' => "\n", '&nbsp;' => ' ')));
                $row['first_body'] = Util::shorten_text($row['first_body'], !empty($modSettings['preview_characters']) ? $modSettings['preview_characters'] : 128, true);
                // No reply then they are the same, no need to process it again
                if ($row['num_replies'] == 0) {
                    $row['last_body'] == $row['first_body'];
                } else {
                    $row['last_body'] = strip_tags(strtr(parse_bbc($row['last_body'], false, $row['id_last_msg']), array('<br />' => "\n", '&nbsp;' => ' ')));
                    $row['last_body'] = Util::shorten_text($row['last_body'], !empty($modSettings['preview_characters']) ? $modSettings['preview_characters'] : 128, true);
                }
                // Censor the subject and message preview.
                censorText($row['first_subject']);
                censorText($row['first_body']);
                // Don't censor them twice!
                if ($row['id_first_msg'] == $row['id_last_msg']) {
                    $row['last_subject'] = $row['first_subject'];
                    $row['last_body'] = $row['first_body'];
                } else {
                    censorText($row['last_subject']);
                    censorText($row['last_body']);
                }
            } else {
                $row['first_body'] = '';
                $row['last_body'] = '';
                censorText($row['first_subject']);
                if ($row['id_first_msg'] == $row['id_last_msg']) {
                    $row['last_subject'] = $row['first_subject'];
                } else {
                    censorText($row['last_subject']);
                }
            }
            // Decide how many pages the topic should have.
            $topic_length = $row['num_replies'] + 1;
            $messages_per_page = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
            if ($topic_length > $messages_per_page) {
                $start = -1;
                $pages = constructPageIndex($scripturl . '?topic=' . $row['id_topic'] . '.%1$d;topicseen', $start, $topic_length, $messages_per_page, true, array('prev_next' => false, 'all' => !empty($modSettings['enableAllMessages']) && $topic_length < $modSettings['enableAllMessages']));
            } else {
                $pages = '';
            }
            // We need to check the topic icons exist... you can never be too sure!
            if (!empty($modSettings['messageIconChecks_enable'])) {
                // First icon first... as you'd expect.
                if (!isset($context['icon_sources'][$row['first_icon']])) {
                    $context['icon_sources'][$row['first_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['first_icon'] . '.png') ? 'images_url' : 'default_images_url';
                }
                // Last icon... last... duh.
                if (!isset($context['icon_sources'][$row['last_icon']])) {
                    $context['icon_sources'][$row['last_icon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['last_icon'] . '.png') ? 'images_url' : 'default_images_url';
                }
            }
            // And build the array.
            $context['topics'][$row['id_topic']] = array('id' => $row['id_topic'], 'first_post' => array('id' => $row['id_first_msg'], 'member' => array('name' => $row['first_poster_name'], 'id' => $row['id_first_member'], 'href' => $scripturl . '?action=profile;u=' . $row['id_first_member'], 'link' => !empty($row['id_first_member']) ? '<a class="preview" href="' . $scripturl . '?action=profile;u=' . $row['id_first_member'] . '" title="' . $txt['profile_of'] . ' ' . $row['first_poster_name'] . '">' . $row['first_poster_name'] . '</a>' : $row['first_poster_name']), 'time' => standardTime($row['first_poster_time']), 'html_time' => htmlTime($row['first_poster_time']), 'timestamp' => forum_time(true, $row['first_poster_time']), 'subject' => $row['first_subject'], 'preview' => $row['first_body'], 'icon' => $row['first_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.png', 'href' => $scripturl . '?topic=' . $row['id_topic'] . '.0;topicseen', 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.0;topicseen">' . $row['first_subject'] . '</a>'), 'last_post' => array('id' => $row['id_last_msg'], 'member' => array('name' => $row['last_poster_name'], 'id' => $row['id_last_member'], 'href' => $scripturl . '?action=profile;u=' . $row['id_last_member'], 'link' => !empty($row['id_last_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_last_member'] . '">' . $row['last_poster_name'] . '</a>' : $row['last_poster_name']), 'time' => standardTime($row['last_poster_time']), 'html_time' => htmlTime($row['last_poster_time']), 'timestamp' => forum_time(true, $row['last_poster_time']), 'subject' => $row['last_subject'], 'preview' => $row['last_body'], 'icon' => $row['last_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['last_icon']]] . '/post/' . $row['last_icon'] . '.png', 'href' => $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['id_last_msg']) . ';topicseen#msg' . $row['id_last_msg'], 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['id_last_msg']) . ';topicseen#msg' . $row['id_last_msg'] . '" rel="nofollow">' . $row['last_subject'] . '</a>'), 'default_preview' => trim($row[!empty($modSettings['message_index_preview']) && $modSettings['message_index_preview'] == 2 ? 'last_body' : 'first_body']), 'new_from' => $row['new_from'], 'new_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['new_from'] . ';topicseen#new', 'href' => $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['new_from']) . ';topicseen' . ($row['num_replies'] == 0 ? '' : 'new'), 'link' => '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . ($row['num_replies'] == 0 ? '.0' : '.msg' . $row['new_from']) . ';topicseen#msg' . $row['new_from'] . '" rel="nofollow">' . $row['first_subject'] . '</a>', 'is_sticky' => !empty($modSettings['enableStickyTopics']) && !empty($row['is_sticky']), 'is_locked' => !empty($row['locked']), 'is_poll' => !empty($modSettings['pollMode']) && $row['id_poll'] > 0, 'is_hot' => !empty($modSettings['useLikesNotViews']) ? $row['num_likes'] >= $modSettings['hotTopicPosts'] : $row['num_replies'] >= $modSettings['hotTopicPosts'], 'is_very_hot' => !empty($modSettings['useLikesNotViews']) ? $row['num_likes'] >= $modSettings['hotTopicVeryPosts'] : $row['num_replies'] >= $modSettings['hotTopicVeryPosts'], 'is_posted_in' => false, 'icon' => $row['first_icon'], 'icon_url' => $settings[$context['icon_sources'][$row['first_icon']]] . '/post/' . $row['first_icon'] . '.png', 'subject' => $row['first_subject'], 'pages' => $pages, 'replies' => comma_format($row['num_replies']), 'views' => comma_format($row['num_views']), 'likes' => comma_format($row['num_likes']), 'board' => array('id' => $row['id_board'], 'name' => $row['bname'], 'href' => $scripturl . '?board=' . $row['id_board'] . '.0', 'link' => '<a href="' . $scripturl . '?board=' . $row['id_board'] . '.0">' . $row['bname'] . '</a>'));
            $context['topics'][$row['id_topic']]['first_post']['started_by'] = sprintf($txt['topic_started_by_in'], '<strong>' . $context['topics'][$row['id_topic']]['first_post']['member']['link'] . '</strong>', '<em>' . $context['topics'][$row['id_topic']]['board']['link'] . '</em>');
            determineTopicClass($context['topics'][$row['id_topic']]);
        }
        $db->free_result($request);
        if ($is_topics && !empty($modSettings['enableParticipation']) && !empty($topic_ids)) {
            require_once SUBSDIR . '/MessageIndex.subs.php';
            $topics_participated_in = topicsParticipation($user_info['id'], $topic_ids);
            foreach ($topics_participated_in as $topic) {
                if (empty($context['topics'][$topic['id_topic']]['is_posted_in'])) {
                    $context['topics'][$topic['id_topic']]['is_posted_in'] = true;
                    $context['topics'][$topic['id_topic']]['class'] = 'my_' . $context['topics'][$topic['id_topic']]['class'];
                }
            }
        }
        $context['querystring_board_limits'] = sprintf($context['querystring_board_limits'], $_REQUEST['start']);
        $context['topics_to_mark'] = implode('-', $topic_ids);
        if ($settings['show_mark_read']) {
            // Build the recent button array.
            if ($is_topics) {
                $context['recent_buttons'] = array('markread' => array('text' => !empty($context['no_board_limits']) ? 'mark_as_read' : 'mark_read_short', 'image' => 'markread.png', 'lang' => true, 'custom' => 'onclick="return markunreadButton(this);"', 'url' => $scripturl . '?action=markasread;sa=' . (!empty($context['no_board_limits']) ? 'all' : 'board' . $context['querystring_board_limits']) . ';' . $context['session_var'] . '=' . $context['session_id']));
                if ($context['showCheckboxes']) {
                    $context['recent_buttons']['markselectread'] = array('text' => 'quick_mod_markread', 'image' => 'markselectedread.png', 'lang' => true, 'url' => 'javascript:document.quickModForm.submit();');
                }
                if (!empty($context['topics']) && !$context['showing_all_topics']) {
                    $context['recent_buttons']['readall'] = array('text' => 'unread_topics_all', 'image' => 'markreadall.png', 'lang' => true, 'url' => $scripturl . '?action=unread;all' . $context['querystring_board_limits'], 'active' => true);
                }
            } elseif (!$is_topics && isset($context['topics_to_mark'])) {
                $context['recent_buttons'] = array('markread' => array('text' => 'mark_as_read', 'image' => 'markread.png', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=unreadreplies;topics=' . $context['topics_to_mark'] . ';' . $context['session_var'] . '=' . $context['session_id']));
                if ($context['showCheckboxes']) {
                    $context['recent_buttons']['markselectread'] = array('text' => 'quick_mod_markread', 'image' => 'markselectedread.png', 'lang' => true, 'url' => 'javascript:document.quickModForm.submit();');
                }
            }
            // Allow mods to add additional buttons here
            call_integration_hook('integrate_recent_buttons');
        }
        // Allow helpdesks and bug trackers and what not to add their own unread data (just add a template_layer to show custom stuff in the template!)
        call_integration_hook('integrate_unread_list');
    }