Ejemplo n.º 1
0
function PlushSearch2()
{
    global $scripturl, $modSettings, $sourcedir, $txt, $db_prefix, $db_connection;
    global $user_info, $ID_MEMBER, $context, $options, $messages_request, $boards_can;
    global $excludedWords, $participants, $func;
    // !!! Add spam protection.
    if (!empty($context['load_average']) && !empty($modSettings['loadavg_search']) && $context['load_average'] >= $modSettings['loadavg_search']) {
        fatal_lang_error('loadavg_search_disabled', false);
    }
    // No, no, no... this is a bit hard on the server, so don't you go prefetching it!
    if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') {
        ob_end_clean();
        header('HTTP/1.1 403 Forbidden');
        die;
    }
    $weight_factors = array('frequency', 'age', 'length', 'subject', 'first_message', 'sticky');
    $weight = array();
    $weight_total = 0;
    foreach ($weight_factors as $weight_factor) {
        $weight[$weight_factor] = empty($modSettings['search_weight_' . $weight_factor]) ? 0 : (int) $modSettings['search_weight_' . $weight_factor];
        $weight_total += $weight[$weight_factor];
    }
    // Zero weight.  Weightless :P.
    if (empty($weight_total)) {
        fatal_lang_error('search_invalid_weights');
    }
    // These vars don't require an interface, the're just here for tweaking.
    $recentPercentage = 0.3;
    $humungousTopicPosts = 200;
    $maxMembersToSearch = 500;
    $maxMessageResults = empty($modSettings['search_max_results']) ? 0 : $modSettings['search_max_results'] * 5;
    // Start with no errors.
    $context['search_errors'] = array();
    // Number of pages hard maximum - normally not set at all.
    $modSettings['search_max_results'] = empty($modSettings['search_max_results']) ? 200 * $modSettings['search_results_per_page'] : (int) $modSettings['search_max_results'];
    loadLanguage('Search');
    loadTemplate('Search');
    // Are you allowed?
    isAllowedTo('search_posts');
    require_once $sourcedir . '/Display.php';
    if (!empty($modSettings['search_index']) && $modSettings['search_index'] == 'fulltext') {
        // Try to determine the minimum number of letters for a fulltext search.
        $request = db_query("\n\t\t\tSHOW VARIABLES\n\t\t\tLIKE 'ft_min_word_len'", false, false);
        if ($request !== false && mysql_num_rows($request) == 1) {
            list(, $min_word_length) = mysql_fetch_row($request);
            mysql_free_result($request);
        } else {
            $min_word_length = '4';
        }
        // Some MySQL versions are superior to others :P.
        $canDoBooleanSearch = version_compare(mysql_get_server_info($db_connection), '4.0.1', '>=') == 1;
        // Get a list of banned fulltext words.
        $banned_words = empty($modSettings['search_banned_words']) ? array() : explode(',', addslashes($modSettings['search_banned_words']));
    } elseif (!empty($modSettings['search_index']) && $modSettings['search_index'] == 'custom' && !empty($modSettings['search_custom_index_config'])) {
        $customIndexSettings = unserialize($modSettings['search_custom_index_config']);
        $min_word_length = $customIndexSettings['bytes_per_word'];
        $banned_words = empty($modSettings['search_stopwords']) ? array() : explode(',', addslashes($modSettings['search_stopwords']));
    } else {
        $modSettings['search_index'] = '';
    }
    // $search_params will carry all settings that differ from the default search parameters.
    // That way, the URLs involved in a search page will be kept as short as possible.
    $search_params = array();
    if (isset($_REQUEST['params'])) {
        $temp_params = explode('|"|', base64_decode(strtr($_REQUEST['params'], array(' ' => '+'))));
        foreach ($temp_params as $i => $data) {
            @(list($k, $v) = explode('|\'|', $data));
            $search_params[$k] = stripslashes($v);
        }
        if (isset($search_params['brd'])) {
            $search_params['brd'] = empty($search_params['brd']) ? array() : explode(',', $search_params['brd']);
        }
    }
    // Store whether simple search was used (needed if the user wants to do another query).
    if (!isset($search_params['advanced'])) {
        $search_params['advanced'] = empty($_REQUEST['advanced']) ? 0 : 1;
    }
    // 1 => 'allwords' (default, don't set as param) / 2 => 'anywords'.
    if (!empty($search_params['searchtype']) || !empty($_REQUEST['searchtype']) && $_REQUEST['searchtype'] == 2) {
        $search_params['searchtype'] = 2;
    }
    // Minimum age of messages. Default to zero (don't set param in that case).
    if (!empty($search_params['minage']) || !empty($_REQUEST['minage']) && $_REQUEST['minage'] > 0) {
        $search_params['minage'] = !empty($search_params['minage']) ? (int) $search_params['minage'] : (int) $_REQUEST['minage'];
    }
    // Maximum age of messages. Default to infinite (9999 days: param not set).
    if (!empty($search_params['maxage']) || !empty($_REQUEST['maxage']) && $_REQUEST['maxage'] != 9999) {
        $search_params['maxage'] = !empty($search_params['maxage']) ? (int) $search_params['maxage'] : (int) $_REQUEST['maxage'];
    }
    // Searching a specific topic?
    if (!empty($_REQUEST['topic'])) {
        $search_params['topic'] = (int) $_REQUEST['topic'];
        $search_params['show_complete'] = true;
    } elseif (!empty($search_params['topic'])) {
        $search_params['topic'] = (int) $search_params['topic'];
    }
    if (!empty($search_params['minage']) || !empty($search_params['maxage'])) {
        $request = db_query("\n\t\t\tSELECT " . (empty($search_params['maxage']) ? '0, ' : 'IFNULL(MIN(ID_MSG), -1), ') . (empty($search_params['minage']) ? '0' : 'IFNULL(MAX(ID_MSG), -1)') . "\n\t\t\tFROM {$db_prefix}messages\n\t\t\tWHERE " . (empty($search_params['minage']) ? '1' : 'posterTime <= ' . (time() - 86400 * $search_params['minage'])) . (empty($search_params['maxage']) ? '' : "\n\t\t\t\tAND posterTime >= " . (time() - 86400 * $search_params['maxage'])), __FILE__, __LINE__);
        list($minMsgID, $maxMsgID) = mysql_fetch_row($request);
        if ($minMsgID < 0 || $maxMsgID < 0) {
            $context['search_errors']['no_messages_in_time_frame'] = true;
        }
        mysql_free_result($request);
    }
    // Default the user name to a wildcard matching every user (*).
    if (!empty($search_params['userspec']) || !empty($_REQUEST['userspec']) && $_REQUEST['userspec'] != '*') {
        $search_params['userspec'] = isset($search_params['userspec']) ? $search_params['userspec'] : $_REQUEST['userspec'];
    }
    // If there's no specific user, then don't mention it in the main query.
    if (empty($search_params['userspec'])) {
        $userQuery = '';
    } else {
        $userString = strtr(addslashes($func['htmlspecialchars'](stripslashes($search_params['userspec']), ENT_QUOTES)), array('&quot;' => '"'));
        $userString = strtr($userString, array('%' => '\\%', '_' => '\\_', '*' => '%', '?' => '_'));
        preg_match_all('~"([^"]+)"~', $userString, $matches);
        $possible_users = array_merge($matches[1], explode(',', preg_replace('~"([^"]+)"~', '', $userString)));
        for ($k = 0, $n = count($possible_users); $k < $n; $k++) {
            $possible_users[$k] = trim($possible_users[$k]);
            if (strlen($possible_users[$k]) == 0) {
                unset($possible_users[$k]);
            }
        }
        // Retrieve a list of possible members.
        $request = db_query("\n\t\t\tSELECT ID_MEMBER\n\t\t\tFROM {$db_prefix}members\n\t\t\tWHERE realName LIKE '" . implode("' OR realName LIKE '", $possible_users) . "'", __FILE__, __LINE__);
        // Simply do nothing if there're too many members matching the criteria.
        if (mysql_num_rows($request) > $maxMembersToSearch) {
            $userQuery = '';
        } elseif (mysql_num_rows($request) == 0) {
            $userQuery = "m.ID_MEMBER = 0 AND (m.posterName LIKE '" . implode("' OR m.posterName LIKE '", $possible_users) . "')";
        } else {
            $memberlist = array();
            while ($row = mysql_fetch_assoc($request)) {
                $memberlist[] = $row['ID_MEMBER'];
            }
            $userQuery = "(m.ID_MEMBER IN (" . implode(', ', $memberlist) . ") OR (m.ID_MEMBER = 0 AND (m.posterName LIKE '" . implode("' OR m.posterName LIKE '", $possible_users) . "')))";
        }
        mysql_free_result($request);
    }
    // If the boards were passed by URL (params=), temporarily put them back in $_REQUEST.
    if (!empty($search_params['brd']) && is_array($search_params['brd'])) {
        $_REQUEST['brd'] = $search_params['brd'];
    }
    // Ensure that brd is an array.
    if (!empty($_REQUEST['brd']) && !is_array($_REQUEST['brd'])) {
        $_REQUEST['brd'] = strpos($_REQUEST['brd'], ',') !== false ? explode(',', $_REQUEST['brd']) : array($_REQUEST['brd']);
    }
    // Make sure all boards are integers.
    if (!empty($_REQUEST['brd'])) {
        foreach ($_REQUEST['brd'] as $id => $brd) {
            $_REQUEST['brd'][$id] = (int) $brd;
        }
    }
    // Special case for boards: searching just one topic?
    if (!empty($search_params['topic'])) {
        $request = db_query("\n\t\t\tSELECT b.ID_BOARD\n\t\t\tFROM ({$db_prefix}topics AS t, {$db_prefix}boards AS b)\n\t\t\tWHERE b.ID_BOARD = t.ID_BOARD\n\t\t\t\tAND t.ID_TOPIC = " . $search_params['topic'] . "\n\t\t\t\tAND {$user_info['query_see_board']}\n\t\t\tLIMIT 1", __FILE__, __LINE__);
        if (mysql_num_rows($request) == 0) {
            fatal_lang_error('topic_gone', false);
        }
        $search_params['brd'] = array();
        list($search_params['brd'][0]) = mysql_fetch_row($request);
        mysql_free_result($request);
    } elseif ($user_info['is_admin'] && (!empty($search_params['advanced']) || !empty($_REQUEST['brd']))) {
        $search_params['brd'] = empty($_REQUEST['brd']) ? array() : $_REQUEST['brd'];
    } else {
        $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']}" . (empty($_REQUEST['brd']) ? !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? "\n\t\t\t\tAND b.ID_BOARD != {$modSettings['recycle_board']}" : '' : "\n\t\t\t\tAND b.ID_BOARD IN (" . implode(', ', $_REQUEST['brd']) . ")"), __FILE__, __LINE__);
        $search_params['brd'] = array();
        while ($row = mysql_fetch_assoc($request)) {
            $search_params['brd'][] = $row['ID_BOARD'];
        }
        mysql_free_result($request);
        // This error should pro'bly only happen for hackers.
        if (empty($search_params['brd'])) {
            $context['search_errors']['no_boards_selected'] = true;
        }
    }
    if (count($search_params['brd']) != 0) {
        // If we've selected all boards, this parameter can be left empty.
        $request = db_query("\n\t\t\tSELECT COUNT(*)\n\t\t\tFROM {$db_prefix}boards", __FILE__, __LINE__);
        list($num_boards) = mysql_fetch_row($request);
        mysql_free_result($request);
        if (count($search_params['brd']) == $num_boards) {
            $boardQuery = '';
        } elseif (count($search_params['brd']) == $num_boards - 1 && !empty($modSettings['recycle_board']) && !in_array($modSettings['recycle_board'], $search_params['brd'])) {
            $boardQuery = '!= ' . $modSettings['recycle_board'];
        } else {
            $boardQuery = 'IN (' . implode(', ', $search_params['brd']) . ')';
        }
    } else {
        $boardQuery = '';
    }
    $search_params['show_complete'] = !empty($search_params['show_complete']) || !empty($_REQUEST['show_complete']);
    $search_params['subject_only'] = !empty($search_params['subject_only']) || !empty($_REQUEST['subject_only']);
    $context['compact'] = !$search_params['show_complete'];
    // Get the sorting parameters right. Default to sort by relevance descending.
    $sort_columns = array('relevance', 'numReplies', 'ID_MSG');
    if (empty($search_params['sort']) && !empty($_REQUEST['sort'])) {
        list($search_params['sort'], $search_params['sort_dir']) = array_pad(explode('|', $_REQUEST['sort']), 2, '');
    }
    $search_params['sort'] = !empty($search_params['sort']) && in_array($search_params['sort'], $sort_columns) ? $search_params['sort'] : 'relevance';
    if (!empty($search_params['topic']) && $search_params['sort'] === 'numReplies') {
        $search_params['sort'] = 'ID_MSG';
    }
    // Sorting direction: descending unless stated otherwise.
    $search_params['sort_dir'] = !empty($search_params['sort_dir']) && $search_params['sort_dir'] == 'asc' ? 'asc' : 'desc';
    // Determine some values needed to calculate the relevance.
    $minMsg = (int) ((1 - $recentPercentage) * $modSettings['maxMsgID']);
    $recentMsg = $modSettings['maxMsgID'] - $minMsg;
    // *** Parse the search query
    // Unfortunately, searching for words like this is going to be slow, so we're blacklisting them.
    // !!! Setting to add more here?
    // !!! Maybe only blacklist if they are the only word, or "any" is used?
    $blacklisted_words = array('img', 'url', 'quote', 'www', 'http', 'the', 'is', 'it', 'are', 'if');
    // What are we searching for?
    if (empty($search_params['search'])) {
        if (isset($_GET['search'])) {
            $search_params['search'] = un_htmlspecialchars($_GET['search']);
        } elseif (isset($_POST['search'])) {
            $search_params['search'] = stripslashes($_POST['search']);
        } else {
            $search_params['search'] = '';
        }
    }
    // Nothing??
    if (!isset($search_params['search']) || $search_params['search'] == '') {
        $context['search_errors']['invalid_search_string'] = true;
    }
    // Change non-word characters into spaces.
    $stripped_query = preg_replace('~([\\x0B\\0' . ($context['utf8'] ? $context['server']['complex_preg_chars'] ? '\\x{A0}' : pack('C*', 0xc2, 0xa0) : '\\xA0') . '\\t\\r\\s\\n(){}\\[\\]<>!@$%^*.,:+=`\\~\\?/\\\\]|&(amp|lt|gt|quot);)+~' . ($context['utf8'] ? 'u' : ''), ' ', $search_params['search']);
    // Make the query lower case. It's gonna be case insensitive anyway.
    $stripped_query = un_htmlspecialchars($func['strtolower']($stripped_query));
    // This (hidden) setting will do fulltext searching in the most basic way.
    if (!empty($modSettings['search_simple_fulltext'])) {
        $stripped_query = strtr($stripped_query, array('"' => ''));
    }
    $no_regexp = preg_match('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', $stripped_query) === 1;
    // Extract phrase parts first (e.g. some words "this is a phrase" some more words.)
    preg_match_all('/(?:^|\\s)([-]?)"([^"]+)"(?:$|\\s)/', $stripped_query, $matches, PREG_PATTERN_ORDER);
    $phraseArray = $matches[2];
    // Remove the phrase parts and extract the words.
    $wordArray = explode(' ', preg_replace('~(?:^|\\s)([-]?)"([^"]+)"(?:$|\\s)~' . ($context['utf8'] ? 'u' : ''), ' ', $stripped_query));
    // A minus sign in front of a word excludes the word.... so...
    $excludedWords = array();
    $excludedIndexWords = array();
    $excludedSubjectWords = array();
    $excludedPhrases = array();
    // .. first, we check for things like -"some words", but not "-some words".
    foreach ($matches[1] as $index => $word) {
        if ($word === '-') {
            if (($word = trim($phraseArray[$index], '-_\' ')) !== '' && !in_array($word, $blacklisted_words)) {
                $excludedWords[] = addslashes($word);
            }
            unset($phraseArray[$index]);
        }
    }
    // Now we look for -test, etc.... normaller.
    foreach ($wordArray as $index => $word) {
        if (strpos(trim($word), '-') === 0) {
            if (($word = trim($word, '-_\' ')) !== '' && !in_array($word, $blacklisted_words)) {
                $excludedWords[] = addslashes($word);
            }
            unset($wordArray[$index]);
        }
    }
    // The remaining words and phrases are all included.
    $searchArray = array_merge($phraseArray, $wordArray);
    // Trim everything and make sure there are no words that are the same.
    foreach ($searchArray as $index => $value) {
        if (($searchArray[$index] = trim($value, '-_\' ')) === '' || in_array($searchArray[$index], $blacklisted_words)) {
            unset($searchArray[$index]);
        } else {
            $searchArray[$index] = addslashes($searchArray[$index]);
        }
    }
    $searchArray = array_slice(array_unique($searchArray), 0, 10);
    // Create an array of replacements for highlighting.
    $context['mark'] = array();
    foreach ($searchArray as $word) {
        $context['mark'][$word] = '<b class="highlight">' . $word . '</b>';
    }
    // Initialize two arrays storing the words that have to be searched for.
    $orParts = array();
    $searchWords = array();
    // Make sure at least one word is being searched for.
    if (empty($searchArray)) {
        $context['search_errors']['invalid_search_string'] = true;
    } elseif (empty($search_params['searchtype'])) {
        $orParts[0] = $searchArray;
    } else {
        foreach ($searchArray as $index => $value) {
            $orParts[$index] = array($value);
        }
    }
    // Make sure the excluded words are in all or-branches.
    foreach ($orParts as $orIndex => $andParts) {
        foreach ($excludedWords as $word) {
            $orParts[$orIndex][] = $word;
        }
    }
    // Determine the or-branches and the fulltext search words.
    foreach ($orParts as $orIndex => $andParts) {
        $searchWords[$orIndex] = array('indexed_words' => array(), 'words' => array(), 'subject_words' => array(), 'all_words' => array());
        // Sort the indexed words (large words -> small words -> excluded words).
        if (!empty($modSettings['search_index'])) {
            usort($orParts[$orIndex], 'searchSort');
        }
        foreach ($orParts[$orIndex] as $word) {
            $is_excluded = in_array($word, $excludedWords);
            $searchWords[$orIndex]['all_words'][] = $word;
            $subjectWords = text2words(stripslashes($word));
            if (!$is_excluded || count($subjectWords) === 1) {
                $searchWords[$orIndex]['subject_words'] = array_merge($searchWords[$orIndex]['subject_words'], $subjectWords);
                if ($is_excluded) {
                    $excludedSubjectWords = array_merge($excludedSubjectWords, $subjectWords);
                }
            } else {
                $excludedPhrases[] = $word;
            }
            if (!empty($modSettings['search_index'])) {
                $subwords = text2words(stripslashes($word), $modSettings['search_index'] === 'fulltext' ? null : $min_word_length, $modSettings['search_index'] === 'custom');
                if (($modSettings['search_index'] === 'custom' || $modSettings['search_index'] === 'fulltext' && !$canDoBooleanSearch && count($subwords) > 1) && empty($modSettings['search_force_index'])) {
                    $searchWords[$orIndex]['words'][] = $word;
                }
                if ($modSettings['search_index'] === 'fulltext' && $canDoBooleanSearch) {
                    $fulltextWord = count($subwords) === 1 ? $word : '"' . $word . '"';
                    $searchWords[$orIndex]['indexed_words'][] = $fulltextWord;
                    if ($is_excluded) {
                        $excludedIndexWords[] = $fulltextWord;
                    }
                } elseif (count($subwords) > 1 && $is_excluded) {
                    continue;
                } else {
                    $relyOnIndex = true;
                    foreach ($subwords as $subword) {
                        if (($modSettings['search_index'] === 'custom' || strlen(stripslashes($subword)) >= $min_word_length) && !in_array($subword, $banned_words)) {
                            $searchWords[$orIndex]['indexed_words'][] = $subword;
                            if ($is_excluded) {
                                $excludedIndexWords[] = $subword;
                            }
                        } elseif (!in_array($subword, $banned_words)) {
                            $relyOnIndex = false;
                        }
                    }
                    if ($modSettings['search_index'] === 'fulltext' && $canDoBooleanSearch && !$relyOnIndex && empty($modSettings['search_force_index'])) {
                        $searchWords[$orIndex]['words'][] = $word;
                    }
                }
            }
        }
        // Search_force_index requires all AND parts to have at least one fulltext word.
        if (!empty($modSettings['search_force_index']) && empty($searchWords[$orIndex]['indexed_words'])) {
            $context['search_errors']['query_not_specific_enough'] = true;
            break;
        } else {
            $searchWords[$orIndex]['indexed_words'] = array_slice($searchWords[$orIndex]['indexed_words'], 0, 7);
            $searchWords[$orIndex]['subject_words'] = array_slice($searchWords[$orIndex]['subject_words'], 0, 7);
        }
    }
    // *** Spell checking
    $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
    if ($context['show_spellchecking']) {
        // Windows fix.
        ob_start();
        $old = error_reporting(0);
        pspell_new('en');
        $pspell_link = pspell_new($txt['lang_dictionary'], $txt['lang_spelling'], '', strtr($txt['lang_character_set'], array('iso-' => 'iso', 'ISO-' => 'iso')), PSPELL_FAST | PSPELL_RUN_TOGETHER);
        error_reporting($old);
        if (!$pspell_link) {
            $pspell_link = pspell_new('en', '', '', '', PSPELL_FAST | PSPELL_RUN_TOGETHER);
        }
        ob_end_clean();
        $did_you_mean = array('search' => array(), 'display' => array());
        $found_misspelling = false;
        foreach ($searchArray as $word) {
            if (empty($pspell_link)) {
                continue;
            }
            $word = stripslashes($word);
            // Don't check phrases.
            if (preg_match('~^\\w+$~', $word) === 0) {
                $did_you_mean['search'][] = '"' . $word . '"';
                $did_you_mean['display'][] = '&quot;' . $func['htmlspecialchars']($word) . '&quot;';
                continue;
            } elseif (preg_match('~\\d~', $word) === 1) {
                $did_you_mean['search'][] = $word;
                $did_you_mean['display'][] = $func['htmlspecialchars']($word);
                continue;
            } elseif (pspell_check($pspell_link, $word)) {
                $did_you_mean['search'][] = $word;
                $did_you_mean['display'][] = $func['htmlspecialchars']($word);
                continue;
            }
            $suggestions = pspell_suggest($pspell_link, $word);
            foreach ($suggestions as $i => $s) {
                // Search is case insensitive.
                if ($func['strtolower']($s) == $func['strtolower']($word)) {
                    unset($suggestions[$i]);
                }
            }
            // Anything found?  If so, correct it!
            if (!empty($suggestions)) {
                $suggestions = array_values($suggestions);
                $did_you_mean['search'][] = $suggestions[0];
                $did_you_mean['display'][] = '<em><b>' . $func['htmlspecialchars']($suggestions[0]) . '</b></em>';
                $found_misspelling = true;
            } else {
                $did_you_mean['search'][] = $word;
                $did_you_mean['display'][] = $func['htmlspecialchars']($word);
            }
        }
        if ($found_misspelling) {
            // Don't spell check excluded words, but add them still...
            $temp_excluded = array('search' => array(), 'display' => array());
            foreach ($excludedWords as $word) {
                $word = stripslashes($word);
                if (preg_match('~^\\w+$~', $word) == 0) {
                    $temp_excluded['search'][] = '-"' . $word . '"';
                    $temp_excluded['display'][] = '-&quot;' . $func['htmlspecialchars']($word) . '&quot;';
                } else {
                    $temp_excluded['search'][] = '-' . $word;
                    $temp_excluded['display'][] = '-' . $func['htmlspecialchars']($word);
                }
            }
            $did_you_mean['search'] = array_merge($did_you_mean['search'], $temp_excluded['search']);
            $did_you_mean['display'] = array_merge($did_you_mean['display'], $temp_excluded['display']);
            $temp_params = $search_params;
            $temp_params['search'] = implode(' ', $did_you_mean['search']);
            if (isset($temp_params['brd'])) {
                $temp_params['brd'] = implode(',', $temp_params['brd']);
            }
            $context['params'] = array();
            foreach ($temp_params as $k => $v) {
                $context['did_you_mean_params'][] = $k . '|\'|' . addslashes($v);
            }
            $context['did_you_mean_params'] = base64_encode(implode('|"|', $context['did_you_mean_params']));
            $context['did_you_mean'] = implode(' ', $did_you_mean['display']);
        }
    }
    // Let the user adjust the search query, should they wish?
    $context['search_params'] = $search_params;
    if (isset($context['search_params']['search'])) {
        $context['search_params']['search'] = $func['htmlspecialchars']($context['search_params']['search']);
    }
    if (isset($context['search_params']['userspec'])) {
        $context['search_params']['userspec'] = $func['htmlspecialchars']($context['search_params']['userspec']);
    }
    // *** Encode all search params
    // All search params have been checked, let's compile them to a single string... made less simple by PHP 4.3.9 and below.
    $temp_params = $search_params;
    if (isset($temp_params['brd'])) {
        $temp_params['brd'] = implode(',', $temp_params['brd']);
    }
    $context['params'] = array();
    foreach ($temp_params as $k => $v) {
        $context['params'][] = $k . '|\'|' . addslashes($v);
    }
    $context['params'] = base64_encode(implode('|"|', $context['params']));
    // ... and add the links to the link tree.
    $context['linktree'][] = array('url' => $scripturl . '?action=search;params=' . $context['params'], 'name' => $txt[182]);
    $context['linktree'][] = array('url' => $scripturl . '?action=search2;params=' . $context['params'], 'name' => $txt['search_results']);
    // *** A last error check
    // One or more search errors? Go back to the first search screen.
    if (!empty($context['search_errors'])) {
        $_REQUEST['params'] = $context['params'];
        return PlushSearch1();
    }
    /*	// !!! This doesn't seem too urgent anymore. Can we remove it?
    	if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
    	{
    		// !!! Change error message...
    		if (cache_get_data('search_start:' . ($user_info['is_guest'] ? $user_info['ip'] : $ID_MEMBER), 90) == 1)
    			fatal_lang_error('loadavg_search_disabled', false);
    		cache_put_data('search_start:' . ($user_info['is_guest'] ? $user_info['ip'] : $ID_MEMBER), 1, 90);
    	}*/
    // *** Reserve an ID for caching the search results.
    // Update the cache if the current search term is not yet cached.
    if (empty($_SESSION['search_cache']) || $_SESSION['search_cache']['params'] != $context['params']) {
        // Increase the pointer...
        $modSettings['search_pointer'] = empty($modSettings['search_pointer']) ? 0 : (int) $modSettings['search_pointer'];
        // ...and store it right off.
        updateSettings(array('search_pointer' => $modSettings['search_pointer'] >= 255 ? 0 : $modSettings['search_pointer'] + 1));
        // As long as you don't change the parameters, the cache result is yours.
        $_SESSION['search_cache'] = array('ID_SEARCH' => $modSettings['search_pointer'], 'num_results' => -1, 'params' => $context['params']);
        // Clear the previous cache of the final results cache.
        db_query("\n\t\t\tDELETE FROM {$db_prefix}log_search_results\n\t\t\tWHERE ID_SEARCH = " . $_SESSION['search_cache']['ID_SEARCH'], __FILE__, __LINE__);
        if ($search_params['subject_only']) {
            foreach ($searchWords as $orIndex => $words) {
                $subject_query = array('from' => array("{$db_prefix}topics AS t"), 'left_join' => array(), 'where' => array());
                $numTables = 0;
                $prev_join = 0;
                $numSubjectResults = 0;
                foreach ($words['subject_words'] as $subjectWord) {
                    $numTables++;
                    if (in_array($subjectWord, $excludedSubjectWords)) {
                        $subject_query['left_join'][] = "{$db_prefix}log_search_subjects AS subj{$numTables} ON (subj{$numTables}.word " . (empty($modSettings['search_match_words']) ? "LIKE '%{$subjectWord}%'" : "= '{$subjectWord}'") . " AND subj{$numTables}.ID_TOPIC = t.ID_TOPIC)";
                        $subject_query['where'][] = "(subj{$numTables}.word IS NULL)";
                    } else {
                        $subject_query['from'][] = "{$db_prefix}log_search_subjects AS subj{$numTables}";
                        $subject_query['where'][] = "subj{$numTables}.word " . (empty($modSettings['search_match_words']) ? "LIKE '%{$subjectWord}%'" : "= '{$subjectWord}'");
                        $subject_query['where'][] = "subj{$numTables}.ID_TOPIC = " . ($prev_join === 0 ? 't' : 'subj' . $prev_join) . '.ID_TOPIC';
                        $prev_join = $numTables;
                    }
                }
                if (!empty($userQuery)) {
                    if (!in_array("{$db_prefix}messages AS m", $subject_query['from'])) {
                        $subject_query['from'][] = "{$db_prefix}messages AS m";
                        $subject_query['where'][] = 'm.ID_TOPIC = t.ID_TOPIC';
                    }
                    $subject_query['where'][] = $userQuery;
                }
                if (!empty($search_params['topic'])) {
                    $subject_query['where'][] = 't.ID_TOPIC = ' . $search_params['topic'];
                }
                if (!empty($minMsgID)) {
                    $subject_query['where'][] = 't.ID_FIRST_MSG >= ' . $minMsgID;
                }
                if (!empty($maxMsgID)) {
                    $subject_query['where'][] = 't.ID_LAST_MSG <= ' . $maxMsgID;
                }
                if (!empty($boardQuery)) {
                    $subject_query['where'][] = 't.ID_BOARD ' . $boardQuery;
                }
                if (!empty($excludedPhrases)) {
                    if (!in_array("{$db_prefix}messages AS m", $subject_query['from'])) {
                        $subject_query['from'][] = "{$db_prefix}messages AS m";
                        $subject_query['where'][] = 'm.ID_MSG = t.ID_FIRST_MSG';
                    }
                    foreach ($excludedPhrases as $phrase) {
                        $subject_query['where'][] = 'm.subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . "[[:>:]]'");
                    }
                }
                db_query("\n\t\t\t\t\tINSERT IGNORE INTO {$db_prefix}log_search_results\n\t\t\t\t\t\t(ID_SEARCH, ID_TOPIC, relevance, ID_MSG, num_matches)\n\t\t\t\t\tSELECT \n\t\t\t\t\t\t" . $_SESSION['search_cache']['ID_SEARCH'] . ",\n\t\t\t\t\t\tt.ID_TOPIC,\n\t\t\t\t\t\t1000 * (\n\t\t\t\t\t\t\t{$weight['frequency']} / (t.numReplies + 1) +\n\t\t\t\t\t\t\t{$weight['age']} * IF(t.ID_FIRST_MSG < {$minMsg}, 0, (t.ID_FIRST_MSG - {$minMsg}) / {$recentMsg}) +\n\t\t\t\t\t\t\t{$weight['length']} * IF(t.numReplies < {$humungousTopicPosts}, t.numReplies / {$humungousTopicPosts}, 1) +\n\t\t\t\t\t\t\t{$weight['subject']} +\n\t\t\t\t\t\t\t{$weight['sticky']} * t.isSticky\n\t\t\t\t\t\t) / {$weight_total} AS relevance,\n\t\t\t\t\t\t" . (empty($userQuery) ? 't.ID_FIRST_MSG' : 'm.ID_MSG') . ",\n\t\t\t\t\t\t1\n\t\t\t\t\tFROM (" . implode(', ', $subject_query['from']) . ')' . (empty($subject_query['left_join']) ? '' : "\n\t\t\t\t\t\tLEFT JOIN " . implode("\n\t\t\t\t\t\tLEFT JOIN ", $subject_query['left_join'])) . "\n\t\t\t\t\tWHERE " . implode("\n\t\t\t\t\t\tAND ", $subject_query['where']) . (empty($modSettings['search_max_results']) ? '' : "\n\t\t\t\t\tLIMIT " . ($modSettings['search_max_results'] - $numSubjectResults)), __FILE__, __LINE__);
                $numSubjectResults += db_affected_rows();
                if (!empty($modSettings['search_max_results']) && $numSubjectResults >= $modSettings['search_max_results']) {
                    break;
                }
            }
            $_SESSION['search_cache']['num_results'] = $numSubjectResults;
        } else {
            $main_query = array('select' => array('ID_SEARCH' => $_SESSION['search_cache']['ID_SEARCH'], 'relevance' => '0'), 'weights' => array(), 'from' => array("{$db_prefix}topics AS t", "{$db_prefix}messages AS m"), 'left_join' => array(), 'where' => array('t.ID_TOPIC = m.ID_TOPIC'), 'group_by' => array());
            if (empty($search_params['topic'])) {
                $main_query['select']['ID_TOPIC'] = 't.ID_TOPIC';
                $main_query['select']['ID_MSG'] = 'MAX(m.ID_MSG) AS ID_MSG';
                $main_query['select']['num_matches'] = 'COUNT(*) AS num_matches';
                $main_query['weights'] = array('frequency' => 'COUNT(*) / (t.numReplies + 1)', 'age' => "IF(MAX(m.ID_MSG) < {$minMsg}, 0, (MAX(m.ID_MSG) - {$minMsg}) / {$recentMsg})", 'length' => "IF(t.numReplies < {$humungousTopicPosts}, t.numReplies / {$humungousTopicPosts}, 1)", 'subject' => '0', 'first_message' => "IF(MIN(m.ID_MSG) = t.ID_FIRST_MSG, 1, 0)", 'sticky' => 't.isSticky');
                $main_query['group_by'][] = 't.ID_TOPIC';
            } else {
                // This is outrageous!
                $main_query['select']['ID_TOPIC'] = 'm.ID_MSG AS ID_TOPIC';
                $main_query['select']['ID_MSG'] = 'm.ID_MSG';
                $main_query['select']['num_matches'] = '1 AS num_matches';
                $main_query['weights'] = array('age' => "((m.ID_MSG - t.ID_FIRST_MSG) / IF(t.ID_LAST_MSG = t.ID_FIRST_MSG, 1, t.ID_LAST_MSG - t.ID_FIRST_MSG))", 'first_message' => "IF(m.ID_MSG = t.ID_FIRST_MSG, 1, 0)");
                $main_query['where'][] = 't.ID_TOPIC = ' . $search_params['topic'];
            }
            // *** Get the subject results.
            $numSubjectResults = 0;
            if (empty($search_params['topic'])) {
                // Create a temporary table to store some preliminary results in.
                db_query("\n\t\t\t\t\tDROP TABLE IF EXISTS {$db_prefix}tmp_log_search_topics", __FILE__, __LINE__);
                $createTemporary = db_query("\n\t\t\t\t\tCREATE TEMPORARY TABLE {$db_prefix}tmp_log_search_topics (\n\t\t\t\t\t\tID_TOPIC mediumint(8) unsigned NOT NULL default '0',\n\t\t\t\t\t\tPRIMARY KEY (ID_TOPIC)\n\t\t\t\t\t) TYPE=HEAP", false, false) !== false;
                // Clean up some previous cache.
                if (!$createTemporary) {
                    db_query("\n\t\t\t\t\t\tDELETE FROM {$db_prefix}log_search_topics\n\t\t\t\t\t\tWHERE ID_SEARCH = " . $_SESSION['search_cache']['ID_SEARCH'], __FILE__, __LINE__);
                }
                foreach ($searchWords as $orIndex => $words) {
                    $subject_query = array('from' => array("{$db_prefix}topics AS t"), 'left_join' => array(), 'where' => array());
                    $numTables = 0;
                    $prev_join = 0;
                    foreach ($words['subject_words'] as $subjectWord) {
                        $numTables++;
                        if (in_array($subjectWord, $excludedSubjectWords)) {
                            if (!in_array("{$db_prefix}messages AS m", $subject_query['from'])) {
                                $subject_query['from'][] = "{$db_prefix}messages AS m";
                                $subject_query['where'][] = 'm.ID_MSG = t.ID_FIRST_MSG';
                            }
                            $subject_query['left_join'][] = "{$db_prefix}log_search_subjects AS subj{$numTables} ON (subj{$numTables}.word " . (empty($modSettings['search_match_words']) ? "LIKE '%{$subjectWord}%'" : "= '{$subjectWord}'") . " AND subj{$numTables}.ID_TOPIC = t.ID_TOPIC)";
                            $subject_query['where'][] = "(subj{$numTables}.word IS NULL)";
                            $subject_query['where'][] = 'm.body NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($subjectWord, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $subjectWord), '\\\'') . "[[:>:]]'");
                        } else {
                            $subject_query['from'][] = "{$db_prefix}log_search_subjects AS subj{$numTables}";
                            $subject_query['where'][] = "subj{$numTables}.word " . (empty($modSettings['search_match_words']) ? "LIKE '%{$subjectWord}%'" : "= '{$subjectWord}'");
                            $subject_query['where'][] = "subj{$numTables}.ID_TOPIC = " . ($prev_join === 0 ? 't' : 'subj' . $prev_join) . '.ID_TOPIC';
                            $prev_join = $numTables;
                        }
                    }
                    if (!empty($userQuery)) {
                        if (!in_array("{$db_prefix}messages AS m", $subject_query['from'])) {
                            $subject_query['from'][] = "{$db_prefix}messages AS m";
                            $subject_query['where'][] = 'm.ID_MSG = t.ID_FIRST_MSG';
                        }
                        $subject_query['where'][] = $userQuery;
                    }
                    if (!empty($search_params['topic'])) {
                        $subject_query['where'][] = 't.ID_TOPIC = ' . $search_params['topic'];
                    }
                    if (!empty($minMsgID)) {
                        $subject_query['where'][] = 't.ID_FIRST_MSG >= ' . $minMsgID;
                    }
                    if (!empty($maxMsgID)) {
                        $subject_query['where'][] = 't.ID_LAST_MSG <= ' . $maxMsgID;
                    }
                    if (!empty($boardQuery)) {
                        $subject_query['where'][] = 't.ID_BOARD ' . $boardQuery;
                    }
                    if (!empty($excludedPhrases)) {
                        if (!in_array("{$db_prefix}messages AS m", $subject_query['from'])) {
                            $subject_query['from'][] = "{$db_prefix}messages AS m";
                            $subject_query['where'][] = 'm.ID_MSG = t.ID_FIRST_MSG';
                        }
                        foreach ($excludedPhrases as $phrase) {
                            $subject_query['where'][] = 'm.subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . "[[:>:]]'");
                            $subject_query['where'][] = 'm.body NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . "[[:>:]]'");
                        }
                    }
                    db_query("\n\t\t\t\t\t\tINSERT IGNORE INTO {$db_prefix}" . ($createTemporary ? 'tmp_' : '') . "log_search_topics\n\t\t\t\t\t\t\t(" . ($createTemporary ? '' : 'ID_SEARCH, ') . "ID_TOPIC)\n\t\t\t\t\t\tSELECT " . ($createTemporary ? '' : $_SESSION['search_cache']['ID_SEARCH'] . ', ') . "t.ID_TOPIC\n\t\t\t\t\t\tFROM (" . implode(', ', $subject_query['from']) . ')' . (empty($subject_query['left_join']) ? '' : "\n\t\t\t\t\t\t\tLEFT JOIN " . implode("\n\t\t\t\t\t\t\tLEFT JOIN ", $subject_query['left_join'])) . "\n\t\t\t\t\t\tWHERE " . implode("\n\t\t\t\t\t\t\tAND ", $subject_query['where']) . (empty($modSettings['search_max_results']) ? '' : "\n\t\t\t\t\t\tLIMIT " . ($modSettings['search_max_results'] - $numSubjectResults)), __FILE__, __LINE__);
                    $numSubjectResults += db_affected_rows();
                    if (!empty($modSettings['search_max_results']) && $numSubjectResults >= $modSettings['search_max_results']) {
                        break;
                    }
                }
                if ($numSubjectResults !== 0) {
                    $main_query['weights']['subject'] = 'IF(lst.ID_TOPIC IS NULL, 0, 1)';
                    $main_query['left_join'][] = "{$db_prefix}" . ($createTemporary ? 'tmp_' : '') . "log_search_topics AS lst ON (" . ($createTemporary ? '' : 'lst.ID_SEARCH = ' . $_SESSION['search_cache']['ID_SEARCH'] . ' AND ') . "lst.ID_TOPIC = t.ID_TOPIC)";
                }
            }
            $indexedResults = 0;
            if (!empty($modSettings['search_index'])) {
                db_query("\n\t\t\t\t\tDROP TABLE IF EXISTS {$db_prefix}tmp_log_search_messages", __FILE__, __LINE__);
                $createTemporary = db_query("\n\t\t\t\t\tCREATE TEMPORARY TABLE {$db_prefix}tmp_log_search_messages (\n\t\t\t\t\t\tID_MSG int(10) unsigned NOT NULL default '0',\n\t\t\t\t\t\tPRIMARY KEY (ID_MSG)\n\t\t\t\t\t) TYPE=HEAP", false, false) !== false;
                if (!$createTemporary) {
                    db_query("\n\t\t\t\t\t\tDELETE FROM {$db_prefix}log_search_messages\n\t\t\t\t\t\tWHERE ID_SEARCH = " . $_SESSION['search_cache']['ID_SEARCH'], __FILE__, __LINE__);
                }
                foreach ($searchWords as $orIndex => $words) {
                    // *** Do the fulltext search.
                    if (!empty($words['indexed_words']) && $modSettings['search_index'] == 'fulltext') {
                        $fulltext_query = array('insert_into' => $db_prefix . ($createTemporary ? 'tmp_' : '') . 'log_search_messages', 'select' => array('ID_MSG' => 'ID_MSG'), 'where' => array());
                        if (!$createTemporary) {
                            $fulltext_query['select']['ID_SEARCH'] = $_SESSION['search_cache']['ID_SEARCH'];
                        }
                        if (empty($modSettings['search_simple_fulltext'])) {
                            foreach ($words['words'] as $regularWord) {
                                $fulltext_query['where'][] = 'body' . (in_array($regularWord, $excludedWords) ? ' NOT' : '') . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $regularWord), '\\\'') . "[[:>:]]'");
                            }
                        }
                        if (!empty($userQuery)) {
                            $fulltext_query['where'][] = strtr($userQuery, array('m.' => ''));
                        }
                        if (!empty($search_params['topic'])) {
                            $fulltext_query['where'][] = 'ID_TOPIC = ' . $search_params['topic'];
                        }
                        if (!empty($minMsgID)) {
                            $fulltext_query['where'][] = 'ID_MSG >= ' . $minMsgID;
                        }
                        if (!empty($maxMsgID)) {
                            $fulltext_query['where'][] = 'ID_MSG <= ' . $maxMsgID;
                        }
                        if (!empty($boardQuery)) {
                            $fulltext_query['where'][] = 'ID_BOARD ' . $boardQuery;
                        }
                        if (!empty($excludedPhrases) && empty($modSettings['search_force_index'])) {
                            foreach ($excludedPhrases as $phrase) {
                                $fulltext_query['where'][] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . "[[:>:]]'");
                            }
                        }
                        if (!empty($excludedSubjectWords) && empty($modSettings['search_force_index'])) {
                            foreach ($excludedSubjectWords as $excludedWord) {
                                $fulltext_query['where'][] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($excludedWord, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $excludedWord), '\\\'') . "[[:>:]]'");
                            }
                        }
                        if (!empty($modSettings['search_simple_fulltext'])) {
                            $fulltext_query['where'][] = "MATCH (body) AGAINST ('" . implode(' ', array_diff($words['indexed_words'], $excludedIndexWords)) . "')";
                        } elseif ($canDoBooleanSearch) {
                            $where = "MATCH (body) AGAINST ('";
                            foreach ($words['indexed_words'] as $fulltextWord) {
                                $where .= (in_array($fulltextWord, $excludedIndexWords) ? '-' : '+') . $fulltextWord . ' ';
                            }
                            $fulltext_query['where'][] = substr($where, 0, -1) . "' IN BOOLEAN MODE)";
                        } else {
                            foreach ($words['indexed_words'] as $fulltextWord) {
                                $fulltext_query['where'][] = (in_array($fulltextWord, $excludedIndexWords) ? 'NOT ' : '') . "MATCH (body) AGAINST ('{$fulltextWord}')";
                            }
                        }
                        db_query("\n\t\t\t\t\t\t\tINSERT IGNORE INTO {$fulltext_query['insert_into']}\n\t\t\t\t\t\t\t\t(" . implode(', ', array_keys($fulltext_query['select'])) . ")\n\t\t\t\t\t\t\tSELECT " . implode(', ', $fulltext_query['select']) . "\n\t\t\t\t\t\t\tFROM {$db_prefix}messages\n\t\t\t\t\t\t\tWHERE " . implode("\n\t\t\t\t\t\t\t\tAND ", $fulltext_query['where']) . (empty($maxMessageResults) ? '' : "\n\t\t\t\t\t\t\tLIMIT " . ($maxMessageResults - $indexedResults)), __FILE__, __LINE__);
                        $indexedResults += db_affected_rows();
                        if (!empty($maxMessageResults) && $indexedResults >= $maxMessageResults) {
                            break;
                        }
                    } elseif (!empty($words['indexed_words']) && $modSettings['search_index'] == 'custom') {
                        $custom_query = array('insert_into' => $db_prefix . ($createTemporary ? 'tmp_' : '') . 'log_search_messages', 'select' => array('ID_MSG' => 'm.ID_MSG'), 'from' => array("{$db_prefix}messages AS m"), 'left_join' => array(), 'where' => array());
                        if (!$createTemporary) {
                            $custom_query['select']['ID_SEARCH'] = $_SESSION['search_cache']['ID_SEARCH'];
                        }
                        foreach ($words['words'] as $regularWord) {
                            $custom_query['where'][] = 'm.body' . (in_array($regularWord, $excludedWords) ? ' NOT' : '') . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $regularWord), '\\\'') . "[[:>:]]'");
                        }
                        if (!empty($userQuery)) {
                            $custom_query['where'][] = $userQuery;
                        }
                        if (!empty($search_params['topic'])) {
                            $custom_query['where'][] = 'm.ID_TOPIC = ' . $search_params['topic'];
                        }
                        if (!empty($minMsgID)) {
                            $custom_query['where'][] = 'm.ID_MSG >= ' . $minMsgID;
                        }
                        if (!empty($maxMsgID)) {
                            $custom_query['where'][] = 'm.ID_MSG <= ' . $maxMsgID;
                        }
                        if (!empty($boardQuery)) {
                            $custom_query['where'][] = 'm.ID_BOARD ' . $boardQuery;
                        }
                        if (!empty($excludedPhrases) && empty($modSettings['search_force_index'])) {
                            foreach ($excludedPhrases as $phrase) {
                                $fulltext_query['where'][] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . "[[:>:]]'");
                            }
                        }
                        if (!empty($excludedSubjectWords) && empty($modSettings['search_force_index'])) {
                            foreach ($excludedSubjectWords as $excludedWord) {
                                $fulltext_query['where'][] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($excludedWord, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $excludedWord), '\\\'') . "[[:>:]]'");
                            }
                        }
                        $numTables = 0;
                        $prev_join = 0;
                        foreach ($words['indexed_words'] as $indexedWord) {
                            $numTables++;
                            if (in_array($indexedWord, $excludedIndexWords)) {
                                $custom_query['left_join'][] = "{$db_prefix}log_search_words AS lsw{$numTables} ON (lsw{$numTables}.ID_WORD = {$indexedWord} AND lsw{$numTables}.ID_MSG = m.ID_MSG)";
                                $custom_query['where'][] = "(lsw{$numTables}.ID_WORD IS NULL)";
                            } else {
                                $custom_query['from'][] = "{$db_prefix}log_search_words AS lsw{$numTables}";
                                $custom_query['where'][] = "lsw{$numTables}.ID_WORD = {$indexedWord}";
                                $custom_query['where'][] = "lsw{$numTables}.ID_MSG = " . ($prev_join === 0 ? 'm' : 'lsw' . $prev_join) . '.ID_MSG';
                                $prev_join = $numTables;
                            }
                        }
                        db_query("\n\t\t\t\t\t\t\tINSERT IGNORE INTO {$custom_query['insert_into']}\n\t\t\t\t\t\t\t\t(" . implode(', ', array_keys($custom_query['select'])) . ")\n\t\t\t\t\t\t\tSELECT " . implode(', ', $custom_query['select']) . "\n\t\t\t\t\t\t\tFROM (" . implode(', ', $custom_query['from']) . ')' . (empty($custom_query['left_join']) ? '' : "\n\t\t\t\t\t\t\t\tLEFT JOIN " . implode("\n\t\t\t\t\t\t\t\tLEFT JOIN ", $custom_query['left_join'])) . "\n\t\t\t\t\t\t\tWHERE " . implode("\n\t\t\t\t\t\t\t\tAND ", $custom_query['where']) . (empty($maxMessageResults) ? '' : "\n\t\t\t\t\t\t\tLIMIT " . ($maxMessageResults - $indexedResults)), __FILE__, __LINE__);
                        $indexedResults += db_affected_rows();
                        if (!empty($maxMessageResults) && $indexedResults >= $maxMessageResults) {
                            break;
                        }
                    }
                }
                if (empty($indexedResults) && empty($numSubjectResults) && !empty($modSettings['search_force_index'])) {
                    $context['search_errors']['query_not_specific_enough'] = true;
                    $_REQUEST['params'] = $context['params'];
                    return PlushSearch1();
                } elseif (!empty($indexedResults)) {
                    $main_query['from'][] = $db_prefix . ($createTemporary ? 'tmp_' : '') . 'log_search_messages AS lsm';
                    $main_query['where'][] = 'lsm.ID_MSG = m.ID_MSG';
                    if (!$createTemporary) {
                        $main_query['where'][] = 'lsm.ID_SEARCH = ' . $_SESSION['search_cache']['ID_SEARCH'];
                    }
                }
            } else {
                $orWhere = array();
                foreach ($searchWords as $orIndex => $words) {
                    $where = array();
                    foreach ($words['all_words'] as $regularWord) {
                        $where[] = 'm.body' . (in_array($regularWord, $excludedWords) ? ' NOT' : '') . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $regularWord), '\\\'') . "[[:>:]]'");
                        if (in_array($regularWord, $excludedWords)) {
                            $where[] = 'm.subject NOT' . (empty($modSettings['search_match_words']) || $no_regexp ? " LIKE '%" . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . "%'" : " RLIKE '[[:<:]]" . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $regularWord), '\\\'') . "[[:>:]]'");
                        }
                    }
                    if (!empty($where)) {
                        $orWhere[] = count($where) > 1 ? '(' . implode(' AND ', $where) . ')' : $where[0];
                    }
                }
                if (!empty($orWhere)) {
                    $main_query['where'][] = count($orWhere) > 1 ? '(' . implode(' OR ', $orWhere) . ')' : $orWhere[0];
                }
                if (!empty($userQuery)) {
                    $main_query['where'][] = $userQuery;
                }
                if (!empty($search_params['topic'])) {
                    $main_query['where'][] = 'm.ID_TOPIC = ' . $search_params['topic'];
                }
                if (!empty($minMsgID)) {
                    $main_query['where'][] = 'm.ID_MSG >= ' . $minMsgID;
                }
                if (!empty($maxMsgID)) {
                    $main_query['where'][] = 'm.ID_MSG <= ' . $maxMsgID;
                }
                if (!empty($boardQuery)) {
                    $main_query['where'][] = 'm.ID_BOARD ' . $boardQuery;
                }
            }
            if (!empty($indexedResults) || empty($modSettings['search_index'])) {
                $relevance = '1000 * (';
                $new_weight_total = 0;
                foreach ($main_query['weights'] as $type => $value) {
                    $relevance .= $weight[$type] . ' * ' . $value . ' + ';
                    $new_weight_total += $weight[$type];
                }
                $main_query['select']['relevance'] = substr($relevance, 0, -3) . ") / {$new_weight_total} AS relevance";
                db_query("\n\t\t\t\t\tINSERT IGNORE INTO {$db_prefix}log_search_results\n\t\t\t\t\t\t(" . implode(', ', array_keys($main_query['select'])) . ")\n\t\t\t\t\tSELECT\n\t\t\t\t\t\t" . implode(',
						', $main_query['select']) . "\n\t\t\t\t\tFROM (" . implode(', ', $main_query['from']) . ')' . (empty($main_query['left_join']) ? '' : "\n\t\t\t\t\t\tLEFT JOIN " . implode("\n\t\t\t\t\t\tLEFT JOIN ", $main_query['left_join'])) . "\n\t\t\t\t\tWHERE " . implode("\n\t\t\t\t\t\tAND ", $main_query['where']) . (empty($main_query['group_by']) ? '' : "\n\t\t\t\t\tGROUP BY " . implode(', ', $main_query['group_by'])) . (empty($modSettings['search_max_results']) ? '' : "\n\t\t\t\t\tLIMIT {$modSettings['search_max_results']}"), __FILE__, __LINE__);
                $_SESSION['search_cache']['num_results'] = db_affected_rows();
            }
            // Insert subject-only matches.
            if ($_SESSION['search_cache']['num_results'] < $modSettings['search_max_results'] && $numSubjectResults !== 0) {
                db_query("\n\t\t\t\t\tINSERT IGNORE INTO {$db_prefix}log_search_results\n\t\t\t\t\t\t(ID_SEARCH, ID_TOPIC, relevance, ID_MSG, num_matches)\n\t\t\t\t\tSELECT\n\t\t\t\t\t\t" . $_SESSION['search_cache']['ID_SEARCH'] . ",\n\t\t\t\t\t\tt.ID_TOPIC,\n\t\t\t\t\t\t1000 * (\n\t\t\t\t\t\t\t{$weight['frequency']} / (t.numReplies + 1) +\n\t\t\t\t\t\t\t{$weight['age']} * IF(t.ID_FIRST_MSG < {$minMsg}, 0, (t.ID_FIRST_MSG - {$minMsg}) / {$recentMsg}) +\n\t\t\t\t\t\t\t{$weight['length']} * IF(t.numReplies < {$humungousTopicPosts}, t.numReplies / {$humungousTopicPosts}, 1) +\n\t\t\t\t\t\t\t{$weight['subject']} +\n\t\t\t\t\t\t\t{$weight['sticky']} * t.isSticky\n\t\t\t\t\t\t) / {$weight_total} AS relevance,\n\t\t\t\t\t\tt.ID_FIRST_MSG,\n\t\t\t\t\t\t1\n\t\t\t\t\tFROM ({$db_prefix}topics AS t, {$db_prefix}" . ($createTemporary ? 'tmp_' : '') . "log_search_topics AS lst)\n\t\t\t\t\tWHERE lst.ID_TOPIC = t.ID_TOPIC" . (empty($modSettings['search_max_results']) ? '' : "\n\t\t\t\t\tLIMIT " . ($modSettings['search_max_results'] - $_SESSION['search_cache']['num_results'])), __FILE__, __LINE__);
                $_SESSION['search_cache']['num_results'] += db_affected_rows();
            } elseif ($_SESSION['search_cache']['num_results'] == -1) {
                $_SESSION['search_cache']['num_results'] = 0;
            }
        }
    }
    // *** Retrieve the results to be shown on the page
    $participants = array();
    $request = db_query("\n\t\tSELECT " . (empty($search_params['topic']) ? 'lsr.ID_TOPIC' : $search_params['topic'] . ' AS ID_TOPIC') . ", lsr.ID_MSG, lsr.relevance, lsr.num_matches\n\t\tFROM ({$db_prefix}log_search_results AS lsr" . ($search_params['sort'] == 'numReplies' ? ", {$db_prefix}topics AS t" : '') . ")\n\t\tWHERE ID_SEARCH = " . $_SESSION['search_cache']['ID_SEARCH'] . ($search_params['sort'] == 'numReplies' ? "\n\t\t\tAND t.ID_TOPIC = lsr.ID_TOPIC" : '') . "\n\t\tORDER BY {$search_params['sort']} {$search_params['sort_dir']}\n\t\tLIMIT " . (int) $_REQUEST['start'] . ", {$modSettings['search_results_per_page']}", __FILE__, __LINE__);
    while ($row = mysql_fetch_assoc($request)) {
        $context['topics'][$row['ID_MSG']] = array('id' => $row['ID_TOPIC'], 'relevance' => round($row['relevance'] / 10, 1) . '%', 'num_matches' => $row['num_matches'], 'matches' => array());
        // By default they didn't participate in the topic!
        $participants[$row['ID_TOPIC']] = false;
    }
    mysql_free_result($request);
    // Now that we know how many results to expect we can start calculating the page numbers.
    $context['page_index'] = constructPageIndex($scripturl . '?action=search2;params=' . $context['params'], $_REQUEST['start'], $_SESSION['search_cache']['num_results'], $modSettings['search_results_per_page'], false);
    if (!empty($context['topics'])) {
        // Create an array for the permissions.
        $boards_can = array('post_reply_own' => boardsAllowedTo('post_reply_own'), 'post_reply_any' => boardsAllowedTo('post_reply_any'), 'mark_any_notify' => boardsAllowedTo('mark_any_notify'));
        // How's about some quick moderation?
        if (!empty($options['display_quick_mod']) && !empty($context['topics'])) {
            $boards_can['lock_any'] = boardsAllowedTo('lock_any');
            $boards_can['lock_own'] = boardsAllowedTo('lock_own');
            $boards_can['make_sticky'] = boardsAllowedTo('make_sticky');
            $boards_can['move_any'] = boardsAllowedTo('move_any');
            $boards_can['move_own'] = boardsAllowedTo('move_own');
            $boards_can['remove_any'] = boardsAllowedTo('remove_any');
            $boards_can['remove_own'] = boardsAllowedTo('remove_own');
            $boards_can['merge_any'] = boardsAllowedTo('merge_any');
            $context['can_lock'] = in_array(0, $boards_can['lock_any']);
            $context['can_sticky'] = in_array(0, $boards_can['make_sticky']) && !empty($modSettings['enableStickyTopics']);
            $context['can_move'] = in_array(0, $boards_can['move_any']);
            $context['can_remove'] = in_array(0, $boards_can['remove_any']);
            $context['can_merge'] = in_array(0, $boards_can['merge_any']);
        }
        // Load the posters...
        $request = db_query("\n\t\t\tSELECT ID_MEMBER\n\t\t\tFROM {$db_prefix}messages\n\t\t\tWHERE ID_MEMBER != 0\n\t\t\t\tAND ID_MSG IN (" . implode(', ', array_keys($context['topics'])) . ")\n\t\t\tLIMIT " . count($context['topics']), __FILE__, __LINE__);
        $posters = array();
        while ($row = mysql_fetch_assoc($request)) {
            $posters[] = $row['ID_MEMBER'];
        }
        mysql_free_result($request);
        if (!empty($posters)) {
            loadMemberData(array_unique($posters));
        }
        // Get the messages out for the callback - select enough that it can be made to look just like Display.
        $messages_request = db_query("\n\t\t\tSELECT\n\t\t\t\tm.ID_MSG, m.subject, m.posterName, m.posterEmail, m.posterTime, m.ID_MEMBER,\n\t\t\t\tm.icon, m.posterIP, m.body, m.smileysEnabled, m.modifiedTime, m.modifiedName,\n\t\t\t\tfirst_m.ID_MSG AS first_msg, first_m.subject AS first_subject, first_m.icon AS firstIcon, first_m.posterTime AS first_posterTime,\n\t\t\t\tfirst_mem.ID_MEMBER AS first_member_id, IFNULL(first_mem.realName, first_m.posterName) AS first_member_name,\n\t\t\t\tlast_m.ID_MSG AS last_msg, last_m.posterTime AS last_posterTime, last_mem.ID_MEMBER AS last_member_id,\n\t\t\t\tIFNULL(last_mem.realName, last_m.posterName) AS last_member_name, last_m.icon AS lastIcon, last_m.subject AS last_subject,\n\t\t\t\tt.ID_TOPIC, t.isSticky, t.locked, t.ID_POLL, t.numReplies, t.numViews,\n\t\t\t\tb.ID_BOARD, b.name AS bName, c.ID_CAT, c.name AS cName\n\t\t\tFROM ({$db_prefix}messages AS m, {$db_prefix}topics AS t, {$db_prefix}boards AS b, {$db_prefix}categories AS c, {$db_prefix}messages AS first_m, {$db_prefix}messages AS last_m)\n\t\t\t\tLEFT JOIN {$db_prefix}members AS first_mem ON (first_mem.ID_MEMBER = first_m.ID_MEMBER)\n\t\t\t\tLEFT JOIN {$db_prefix}members AS last_mem ON (last_mem.ID_MEMBER = first_m.ID_MEMBER)\n\t\t\tWHERE m.ID_MSG IN (" . implode(', ', array_keys($context['topics'])) . ")\n\t\t\t\tAND t.ID_TOPIC = m.ID_TOPIC\n\t\t\t\tAND b.ID_BOARD = t.ID_BOARD\n\t\t\t\tAND c.ID_CAT = b.ID_CAT\n\t\t\t\tAND first_m.ID_MSG = t.ID_FIRST_MSG\n\t\t\t\tAND last_m.ID_MSG = t.ID_LAST_MSG\n\t\t\tORDER BY FIND_IN_SET(m.ID_MSG, '" . implode(',', array_keys($context['topics'])) . "')\n\t\t\tLIMIT " . count($context['topics']), __FILE__, __LINE__);
        // Note that the reg-exp slows things alot, but makes things make a lot more sense.
        // If we want to know who participated in what then load this now.
        if (!empty($modSettings['enableParticipation']) && !$user_info['is_guest']) {
            $result = db_query("\n\t\t\t\tSELECT ID_TOPIC\n\t\t\t\tFROM {$db_prefix}messages\n\t\t\t\tWHERE ID_TOPIC IN (" . implode(', ', array_keys($participants)) . ")\n\t\t\t\t\tAND ID_MEMBER = {$ID_MEMBER}\n\t\t\t\tGROUP BY ID_TOPIC\n\t\t\t\tLIMIT " . count($participants), __FILE__, __LINE__);
            while ($row = mysql_fetch_assoc($result)) {
                $participants[$row['ID_TOPIC']] = true;
            }
            mysql_free_result($result);
        }
    }
    // Consider the search complete!
    if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2) {
        cache_put_data('search_start:' . ($user_info['is_guest'] ? $user_info['ip'] : $ID_MEMBER), null, 90);
    }
    $context['key_words'] =& $searchArray;
    // Set the basic stuff for the template.
    $context['allow_hide_email'] = !empty($modSettings['allow_hideEmail']);
    // 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');
    $context['icon_sources'] = array();
    foreach ($stable_icons as $icon) {
        $context['icon_sources'][$icon] = 'images_url';
    }
    $context['sub_template'] = 'results';
    $context['page_title'] = $txt[166];
    $context['get_topics'] = 'prepareSearchContext';
    $context['can_send_pm'] = allowedTo('pm_send');
    loadJumpTo();
    if (!empty($options['display_quick_mod']) && !empty($_SESSION['move_to_topic'])) {
        foreach ($context['jump_to'] as $id => $cat) {
            if (isset($context['jump_to'][$id]['boards'][$_SESSION['move_to_topic']])) {
                $context['jump_to'][$id]['boards'][$_SESSION['move_to_topic']]['selected'] = true;
            }
        }
    }
}
Ejemplo n.º 2
0
function MessageIndex()
{
    global $txt, $scripturl, $board, $db_prefix;
    global $modSettings, $ID_MEMBER;
    global $context, $options, $settings, $board_info, $user_info, $func;
    if (WIRELESS) {
        $context['sub_template'] = WIRELESS_PROTOCOL . '_messageindex';
    } else {
        loadTemplate('MessageIndex');
    }
    $context['name'] = $board_info['name'];
    $context['description'] = $board_info['description'];
    // View all the topics, or just a few?
    $maxindex = isset($_REQUEST['all']) && !empty($modSettings['enableAllMessages']) ? $board_info['num_topics'] : $modSettings['defaultMaxTopics'];
    // Make sure the starting place makes sense and construct the page index.
    if (isset($_REQUEST['sort'])) {
        $context['page_index'] = constructPageIndex($scripturl . '?board=' . $board . '.%d;sort=' . $_REQUEST['sort'] . (isset($_REQUEST['desc']) ? ';desc' : ''), $_REQUEST['start'], $board_info['num_topics'], $maxindex, true);
    } else {
        $context['page_index'] = constructPageIndex($scripturl . '?board=' . $board . '.%d', $_REQUEST['start'], $board_info['num_topics'], $maxindex, true);
    }
    $context['start'] =& $_REQUEST['start'];
    $context['links'] = array('first' => $_REQUEST['start'] >= $modSettings['defaultMaxTopics'] ? $scripturl . '?board=' . $board . '.0' : '', 'prev' => $_REQUEST['start'] >= $modSettings['defaultMaxTopics'] ? $scripturl . '?board=' . $board . '.' . ($_REQUEST['start'] - $modSettings['defaultMaxTopics']) : '', 'next' => $_REQUEST['start'] + $modSettings['defaultMaxTopics'] < $board_info['num_topics'] ? $scripturl . '?board=' . $board . '.' . ($_REQUEST['start'] + $modSettings['defaultMaxTopics']) : '', 'last' => $_REQUEST['start'] + $modSettings['defaultMaxTopics'] < $board_info['num_topics'] ? $scripturl . '?board=' . $board . '.' . floor(($board_info['num_topics'] - 1) / $modSettings['defaultMaxTopics']) * $modSettings['defaultMaxTopics'] : '', 'up' => $board_info['parent'] == 0 ? $scripturl . '?' : $scripturl . '?board=' . $board_info['parent'] . '.0');
    $context['page_info'] = array('current_page' => $_REQUEST['start'] / $modSettings['defaultMaxTopics'] + 1, 'num_pages' => floor(($board_info['num_topics'] - 1) / $modSettings['defaultMaxTopics']) + 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[62] . '">' . $mod['name'] . '</a>';
        }
        $context['linktree'][count($context['linktree']) - 1]['extra_after'] = ' (' . (count($context['link_moderators']) == 1 ? $txt[298] : $txt[299]) . ': ' . implode(', ', $context['link_moderators']) . ')';
    }
    // 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;
        }
        db_query("\n\t\t\tREPLACE INTO {$db_prefix}log_boards\n\t\t\t\t(ID_MSG, ID_MEMBER, ID_BOARD)\n\t\t\tVALUES ({$modSettings['maxMsgID']}, {$ID_MEMBER}, {$board})", __FILE__, __LINE__);
        if (!empty($board_info['parent_boards'])) {
            db_query("\n\t\t\t\tUPDATE {$db_prefix}log_boards\n\t\t\t\tSET ID_MSG = {$modSettings['maxMsgID']}\n\t\t\t\tWHERE ID_MEMBER = {$ID_MEMBER}\n\t\t\t\t\tAND ID_BOARD IN (" . implode(',', array_keys($board_info['parent_boards'])) . ")\n\t\t\t\tLIMIT " . count($board_info['parent_boards']), __FILE__, __LINE__);
            // 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]);
        }
        $request = db_query("\n\t\t\tSELECT sent\n\t\t\tFROM {$db_prefix}log_notify\n\t\t\tWHERE ID_BOARD = {$board}\n\t\t\t\tAND ID_MEMBER = {$ID_MEMBER}\n\t\t\tLIMIT 1", __FILE__, __LINE__);
        $context['is_marked_notify'] = mysql_num_rows($request) != 0;
        if ($context['is_marked_notify']) {
            list($sent) = mysql_fetch_row($request);
            if (!empty($sent)) {
                db_query("\n\t\t\t\t\tUPDATE {$db_prefix}log_notify\n\t\t\t\t\tSET sent = 0\n\t\t\t\t\tWHERE ID_BOARD = {$board}\n\t\t\t\t\t\tAND ID_MEMBER = {$ID_MEMBER}\n\t\t\t\t\tLIMIT 1", __FILE__, __LINE__);
            }
        }
        mysql_free_result($request);
    } 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');
    $context['can_post_poll'] = $modSettings['pollMode'] == '1' && allowedTo('poll_post');
    $context['can_moderate_forum'] = allowedTo('moderate_forum');
    // Aren't children wonderful things?
    $result = db_query("\n\t\tSELECT\n\t\t\tb.ID_BOARD, b.name, b.description, b.numTopics, b.numPosts,\n\t\t\tm.posterName, m.posterTime, m.subject, m.ID_MSG, m.ID_TOPIC,\n\t\t\tIFNULL(mem.realName, m.posterName) AS realName, " . (!$user_info['is_guest'] ? "\n\t\t\t(IFNULL(lb.ID_MSG, 0) >= b.ID_MSG_UPDATED) AS isRead," : "1 AS isRead,") . "\n\t\t\tIFNULL(mem.ID_MEMBER, 0) AS ID_MEMBER, IFNULL(mem2.ID_MEMBER, 0) AS ID_MODERATOR,\n\t\t\tmem2.realName AS modRealName\n\t\tFROM {$db_prefix}boards AS b\n\t\t\tLEFT JOIN {$db_prefix}messages AS m ON (m.ID_MSG = b.ID_LAST_MSG)\n\t\t\tLEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = m.ID_MEMBER)" . (!$user_info['is_guest'] ? "\n\t\t\tLEFT JOIN {$db_prefix}log_boards AS lb ON (lb.ID_BOARD = b.ID_BOARD AND lb.ID_MEMBER = {$ID_MEMBER})" : '') . "\n\t\t\tLEFT JOIN {$db_prefix}moderators AS mods ON (mods.ID_BOARD = b.ID_BOARD)\n\t\t\tLEFT JOIN {$db_prefix}members AS mem2 ON (mem2.ID_MEMBER = mods.ID_MEMBER)\n\t\tWHERE b.ID_PARENT = {$board}\n\t\t\tAND {$user_info['query_see_board']}", __FILE__, __LINE__);
    if (mysql_num_rows($result) != 0) {
        $theboards = array();
        while ($row_board = mysql_fetch_assoc($result)) {
            if (!isset($context['boards'][$row_board['ID_BOARD']])) {
                $theboards[] = $row_board['ID_BOARD'];
                // Make sure the subject isn't too long.
                censorText($row_board['subject']);
                $short_subject = shorten_subject($row_board['subject'], 24);
                $context['boards'][$row_board['ID_BOARD']] = array('id' => $row_board['ID_BOARD'], 'last_post' => array('id' => $row_board['ID_MSG'], 'time' => $row_board['posterTime'] > 0 ? timeformat($row_board['posterTime']) : $txt[470], 'timestamp' => forum_time(true, $row_board['posterTime']), 'subject' => $short_subject, 'member' => array('id' => $row_board['ID_MEMBER'], 'username' => $row_board['posterName'] != '' ? $row_board['posterName'] : $txt[470], 'name' => $row_board['realName'], 'href' => !empty($row_board['ID_MEMBER']) ? $scripturl . '?action=profile;u=' . $row_board['ID_MEMBER'] : '', 'link' => $row_board['posterName'] != '' ? !empty($row_board['ID_MEMBER']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row_board['ID_MEMBER'] . '">' . $row_board['realName'] . '</a>' : $row_board['realName'] : $txt[470]), 'start' => 'new', 'topic' => $row_board['ID_TOPIC'], 'href' => $row_board['subject'] != '' ? $scripturl . '?topic=' . $row_board['ID_TOPIC'] . '.new' . (empty($row_board['isRead']) ? ';boardseen' : '') . '#new' : '', 'link' => $row_board['subject'] != '' ? '<a href="' . $scripturl . '?topic=' . $row_board['ID_TOPIC'] . '.new' . (empty($row_board['isRead']) ? ';boardseen' : '') . '#new" title="' . $row_board['subject'] . '">' . $short_subject . '</a>' : $txt[470]), 'new' => empty($row_board['isRead']) && $row_board['posterName'] != '', 'name' => $row_board['name'], 'description' => $row_board['description'], 'moderators' => array(), 'link_moderators' => array(), 'children' => array(), 'link_children' => array(), 'children_new' => false, 'topics' => $row_board['numTopics'], 'posts' => $row_board['numPosts'], 'href' => $scripturl . '?board=' . $row_board['ID_BOARD'] . '.0', 'link' => '<a href="' . $scripturl . '?board=' . $row_board['ID_BOARD'] . '.0">' . $row_board['name'] . '</a>');
            }
            if (!empty($row_board['ID_MODERATOR'])) {
                $context['boards'][$row_board['ID_BOARD']]['moderators'][$row_board['ID_MODERATOR']] = array('id' => $row_board['ID_MODERATOR'], 'name' => $row_board['modRealName'], 'href' => $scripturl . '?action=profile;u=' . $row_board['ID_MODERATOR'], 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row_board['ID_MODERATOR'] . '" title="' . $txt[62] . '">' . $row_board['modRealName'] . '</a>');
                $context['boards'][$row_board['ID_BOARD']]['link_moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $row_board['ID_MODERATOR'] . '" title="' . $txt[62] . '">' . $row_board['modRealName'] . '</a>';
            }
        }
        mysql_free_result($result);
        // Load up the child boards.
        $result = db_query("\n\t\t\tSELECT\n\t\t\t\tb.ID_BOARD, b.ID_PARENT, b.name, b.description, b.numTopics, b.numPosts,\n\t\t\t\tm.posterName, IFNULL(m.posterTime, 0) AS posterTime, m.subject, m.ID_MSG, m.ID_TOPIC,\n\t\t\t\tIFNULL(mem.realName, m.posterName) AS realName, ID_PARENT, \n\t\t\t\t" . ($user_info['is_guest'] ? '1' : '(IFNULL(lb.ID_MSG, 0) >= b.ID_MSG_UPDATED)') . " AS isRead,\n\t\t\t\tIFNULL(mem.ID_MEMBER, 0) AS ID_MEMBER\n\t\t\tFROM {$db_prefix}boards AS b\n\t\t\t\tLEFT JOIN {$db_prefix}messages AS m ON (m.ID_MSG = b.ID_LAST_MSG)\n\t\t\t\tLEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = m.ID_MEMBER)" . (!$user_info['is_guest'] ? "\n\t\t\t\tLEFT JOIN {$db_prefix}log_boards AS lb ON (lb.ID_BOARD = b.ID_BOARD AND lb.ID_MEMBER = {$ID_MEMBER})" : '') . "\n\t\t\tWHERE " . (empty($modSettings['countChildPosts']) ? "b.ID_PARENT IN (" . implode(',', $theboards) . ")" : "childLevel > 0") . "\n\t\t\t\tAND {$user_info['query_see_board']}", __FILE__, __LINE__);
        $parent_map = array();
        while ($row = mysql_fetch_assoc($result)) {
            // We've got a child of a child, then... possibly.
            if (!in_array($row['ID_PARENT'], $theboards)) {
                if (!isset($parent_map[$row['ID_PARENT']])) {
                    continue;
                }
                $parent_map[$row['ID_PARENT']][0]['posts'] += $row['numPosts'];
                $parent_map[$row['ID_PARENT']][0]['topics'] += $row['numTopics'];
                $parent_map[$row['ID_PARENT']][1]['posts'] += $row['numPosts'];
                $parent_map[$row['ID_PARENT']][1]['topics'] += $row['numTopics'];
                $parent_map[$row['ID_BOARD']] = $parent_map[$row['ID_PARENT']];
                continue;
            }
            if ($context['boards'][$row['ID_PARENT']]['last_post']['timestamp'] < forum_time(true, $row['posterTime'])) {
                // Make sure the subject isn't too long.
                censorText($row['subject']);
                $short_subject = shorten_subject($row['subject'], 24);
                $context['boards'][$row['ID_PARENT']]['last_post'] = array('id' => $row['ID_MSG'], 'time' => $row['posterTime'] > 0 ? timeformat($row['posterTime']) : $txt[470], 'timestamp' => forum_time(true, $row['posterTime']), 'subject' => $short_subject, 'member' => array('username' => $row['posterName'] != '' ? $row['posterName'] : $txt[470], 'name' => $row['realName'], 'id' => $row['ID_MEMBER'], 'href' => !empty($row['ID_MEMBER']) ? $scripturl . '?action=profile;u=' . $row['ID_MEMBER'] : '', 'link' => $row['posterName'] != '' ? !empty($row['ID_MEMBER']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['ID_MEMBER'] . '">' . $row['realName'] . '</a>' : $row['realName'] : $txt[470]), 'start' => 'new', 'topic' => $row['ID_TOPIC'], 'href' => $scripturl . '?topic=' . $row['ID_TOPIC'] . '.new' . (empty($row['isRead']) ? ';boardseen' : '') . '#new');
                $context['boards'][$row['ID_PARENT']]['last_post']['link'] = $row['subject'] != '' ? '<a href="' . $context['boards'][$row['ID_PARENT']]['last_post']['href'] . '" title="' . $row['subject'] . '">' . $short_subject . '</a>' : $txt[470];
            }
            $context['boards'][$row['ID_PARENT']]['children'][$row['ID_BOARD']] = array('id' => $row['ID_BOARD'], 'name' => $row['name'], 'description' => $row['description'], 'new' => empty($row['isRead']) && $row['posterName'] != '', 'topics' => $row['numTopics'], 'posts' => $row['numPosts'], 'href' => $scripturl . '?board=' . $row['ID_BOARD'] . '.0', 'link' => '<a href="' . $scripturl . '?board=' . $row['ID_BOARD'] . '.0">' . $row['name'] . '</a>');
            $context['boards'][$row['ID_PARENT']]['link_children'][] = '<a href="' . $scripturl . '?board=' . $row['ID_BOARD'] . '.0">' . $row['name'] . '</a>';
            $context['boards'][$row['ID_PARENT']]['children_new'] |= empty($row['isRead']) && $row['posterName'] != '';
            if (!empty($modSettings['countChildPosts'])) {
                $context['boards'][$row['ID_PARENT']]['posts'] += $row['numPosts'];
                $context['boards'][$row['ID_PARENT']]['topics'] += $row['numTopics'];
                $parent_map[$row['ID_BOARD']] = array(&$context['boards'][$row['ID_PARENT']], &$context['boards'][$row['ID_PARENT']]['children'][$row['ID_BOARD']]);
            }
        }
    }
    mysql_free_result($result);
    // Nosey, nosey - who's viewing this topic?
    if (!empty($settings['display_who_viewing'])) {
        $context['view_members'] = array();
        $context['view_members_list'] = array();
        $context['view_num_hidden'] = 0;
        $request = db_query("\n\t\t\tSELECT\n\t\t\t\tlo.ID_MEMBER, lo.logTime, mem.realName, mem.memberName, mem.showOnline,\n\t\t\t\tmg.onlineColor, mg.ID_GROUP, mg.groupName\n\t\t\tFROM {$db_prefix}log_online AS lo\n\t\t\t\tLEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = lo.ID_MEMBER)\n\t\t\t\tLEFT JOIN {$db_prefix}membergroups AS mg ON (mg.ID_GROUP = IF(mem.ID_GROUP = 0, mem.ID_POST_GROUP, mem.ID_GROUP))\n\t\t\tWHERE INSTR(lo.url, 's:5:\"board\";i:{$board};') OR lo.session = '" . ($user_info['is_guest'] ? 'ip' . $user_info['ip'] : session_id()) . "'", __FILE__, __LINE__);
        while ($row = mysql_fetch_assoc($request)) {
            if (empty($row['ID_MEMBER'])) {
                continue;
            }
            if (!empty($row['onlineColor'])) {
                $link = '<a href="' . $scripturl . '?action=profile;u=' . $row['ID_MEMBER'] . '" style="color: ' . $row['onlineColor'] . ';">' . $row['realName'] . '</a>';
            } else {
                $link = '<a href="' . $scripturl . '?action=profile;u=' . $row['ID_MEMBER'] . '">' . $row['realName'] . '</a>';
            }
            $is_buddy = in_array($row['ID_MEMBER'], $user_info['buddies']);
            if ($is_buddy) {
                $link = '<b>' . $link . '</b>';
            }
            if (!empty($row['showOnline']) || allowedTo('moderate_forum')) {
                $context['view_members_list'][$row['logTime'] . $row['memberName']] = empty($row['showOnline']) ? '<i>' . $link . '</i>' : $link;
            }
            $context['view_members'][$row['logTime'] . $row['memberName']] = array('id' => $row['ID_MEMBER'], 'username' => $row['memberName'], 'name' => $row['realName'], 'group' => $row['ID_GROUP'], 'href' => $scripturl . '?action=profile;u=' . $row['ID_MEMBER'], 'link' => $link, 'is_buddy' => $is_buddy, 'hidden' => empty($row['showOnline']));
            if (empty($row['showOnline'])) {
                $context['view_num_hidden']++;
            }
        }
        $context['view_num_guests'] = mysql_num_rows($request) - count($context['view_members']);
        mysql_free_result($request);
        // Put them in "last clicked" order.
        krsort($context['view_members_list']);
        krsort($context['view_members']);
    }
    // Default sort methods.
    $sort_methods = array('subject' => 'mf.subject', 'starter' => 'IFNULL(memf.realName, mf.posterName)', 'last_poster' => 'IFNULL(meml.realName, ml.posterName)', 'replies' => 't.numReplies', 'views' => 't.numViews', 'first_post' => 't.ID_TOPIC', 'last_post' => 't.ID_LAST_MSG');
    // 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';
        $_REQUEST['sort'] = 'ID_LAST_MSG';
        $ascending = isset($_REQUEST['asc']);
    } else {
        $context['sort_by'] = $_REQUEST['sort'];
        $_REQUEST['sort'] = $sort_methods[$_REQUEST['sort']];
        $ascending = !isset($_REQUEST['desc']);
    }
    $context['sort_direction'] = $ascending ? 'up' : 'down';
    // Calculate the fastest way to get the topics.
    $start = $_REQUEST['start'];
    if ($start > ($board_info['num_topics'] - 1) / 2) {
        $ascending = !$ascending;
        $fake_ascending = true;
        $maxindex = $board_info['num_topics'] < $start + $maxindex + 1 ? $board_info['num_topics'] - $start : $maxindex;
        $start = $board_info['num_topics'] < $start + $maxindex + 1 ? 0 : $board_info['num_topics'] - $start - $maxindex;
    } else {
        $fake_ascending = false;
    }
    // Setup the default topic icons...
    $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless');
    $context['icon_sources'] = array();
    foreach ($stable_icons as $icon) {
        $context['icon_sources'][$icon] = 'images_url';
    }
    $topic_ids = array();
    $context['topics'] = array();
    // Sequential pages are often not optimized, so we add an additional query.
    $pre_query = $start > 0;
    if ($pre_query) {
        $request = db_query("\n\t\t\tSELECT t.ID_TOPIC\n\t\t\tFROM ({$db_prefix}topics AS t" . ($context['sort_by'] === 'last_poster' ? ", {$db_prefix}messages AS ml" : (in_array($context['sort_by'], array('starter', 'subject')) ? ", {$db_prefix}messages AS mf" : '')) . ')' . ($context['sort_by'] === 'starter' ? "\n\t\t\t\tLEFT JOIN {$db_prefix}members AS memf ON (memf.ID_MEMBER = mf.ID_MEMBER)" : '') . ($context['sort_by'] === 'last_poster' ? "\n\t\t\t\tLEFT JOIN {$db_prefix}members AS meml ON (meml.ID_MEMBER = ml.ID_MEMBER)" : '') . "\n\t\t\tWHERE t.ID_BOARD = {$board}" . ($context['sort_by'] === 'last_poster' ? "\n\t\t\t\tAND ml.ID_MSG = t.ID_LAST_MSG" : (in_array($context['sort_by'], array('starter', 'subject')) ? "\n\t\t\t\tAND mf.ID_MSG = t.ID_FIRST_MSG" : '')) . "\n\t\t\tORDER BY " . (!empty($modSettings['enableStickyTopics']) ? 'isSticky' . ($fake_ascending ? '' : ' DESC') . ', ' : '') . $_REQUEST['sort'] . ($ascending ? '' : ' DESC') . "\n\t\t\tLIMIT {$start}, {$maxindex}", __FILE__, __LINE__);
        $topic_ids = array();
        while ($row = mysql_fetch_assoc($request)) {
            $topic_ids[] = $row['ID_TOPIC'];
        }
    }
    // Grab the appropriate topic information...
    if (!$pre_query || !empty($topic_ids)) {
        $result = db_query("\n\t\t\tSELECT\n\t\t\t\tt.ID_TOPIC, t.numReplies, t.locked, t.numViews, t.isSticky, t.ID_POLL,\n\t\t\t\t" . ($user_info['is_guest'] ? '0' : 'IFNULL(lt.ID_MSG, IFNULL(lmr.ID_MSG, -1)) + 1') . " AS new_from,\n\t\t\t\tt.ID_LAST_MSG, ml.posterTime AS lastPosterTime, ml.ID_MSG_MODIFIED,\n\t\t\t\tml.subject AS lastSubject, ml.icon AS lastIcon, ml.posterName AS lastMemberName,\n\t\t\t\tml.ID_MEMBER AS lastID_MEMBER, IFNULL(meml.realName, ml.posterName) AS lastDisplayName,\n\t\t\t\tt.ID_FIRST_MSG, mf.posterTime AS firstPosterTime,\n\t\t\t\tmf.subject AS firstSubject, mf.icon AS firstIcon, mf.posterName AS firstMemberName,\n\t\t\t\tmf.ID_MEMBER AS firstID_MEMBER, IFNULL(memf.realName, mf.posterName) AS firstDisplayName,\n\t\t\t\tLEFT(ml.body, 384) AS lastBody, LEFT(mf.body, 384) AS firstBody, ml.smileysEnabled AS lastSmileys,\n\t\t\t\tmf.smileysEnabled AS firstSmileys\n\t\t\tFROM ({$db_prefix}topics AS t, {$db_prefix}messages AS ml, {$db_prefix}messages AS mf)\n\t\t\t\tLEFT JOIN {$db_prefix}members AS meml ON (meml.ID_MEMBER = ml.ID_MEMBER)\n\t\t\t\tLEFT JOIN {$db_prefix}members AS memf ON (memf.ID_MEMBER = mf.ID_MEMBER)" . ($user_info['is_guest'] ? '' : "\n\t\t\t\tLEFT JOIN {$db_prefix}log_topics AS lt ON (lt.ID_TOPIC = t.ID_TOPIC AND lt.ID_MEMBER = {$ID_MEMBER})\n\t\t\t\tLEFT JOIN {$db_prefix}log_mark_read AS lmr ON (lmr.ID_BOARD = {$board} AND lmr.ID_MEMBER = {$ID_MEMBER})") . "\n\t\t\tWHERE " . ($pre_query ? 't.ID_TOPIC IN (' . implode(', ', $topic_ids) . ')' : "t.ID_BOARD = {$board}") . "\n\t\t\t\tAND ml.ID_MSG = t.ID_LAST_MSG\n\t\t\t\tAND mf.ID_MSG = t.ID_FIRST_MSG\n\t\t\tORDER BY " . ($pre_query ? "FIND_IN_SET(t.ID_TOPIC, '" . implode(',', $topic_ids) . "')" : (!empty($modSettings['enableStickyTopics']) ? 'isSticky' . ($fake_ascending ? '' : ' DESC') . ', ' : '') . $_REQUEST['sort'] . ($ascending ? '' : ' DESC')) . "\n\t\t\tLIMIT " . ($pre_query ? '' : "{$start}, ") . "{$maxindex}", __FILE__, __LINE__);
        // Begin 'printing' the message index for current board.
        while ($row = mysql_fetch_assoc($result)) {
            if ($row['ID_POLL'] > 0 && $modSettings['pollMode'] == '0') {
                continue;
            }
            if (!$pre_query) {
                $topic_ids[] = $row['ID_TOPIC'];
            }
            // Limit them to 128 characters - do this FIRST because it's a lot of wasted censoring otherwise.
            $row['firstBody'] = strip_tags(strtr(parse_bbc($row['firstBody'], $row['firstSmileys'], $row['ID_FIRST_MSG']), array('<br />' => '&#10;')));
            if ($func['strlen']($row['firstBody']) > 128) {
                $row['firstBody'] = $func['substr']($row['firstBody'], 0, 128) . '...';
            }
            $row['lastBody'] = strip_tags(strtr(parse_bbc($row['lastBody'], $row['lastSmileys'], $row['ID_LAST_MSG']), array('<br />' => '&#10;')));
            if ($func['strlen']($row['lastBody']) > 128) {
                $row['lastBody'] = $func['substr']($row['lastBody'], 0, 128) . '...';
            }
            // Censor the subject and message preview.
            censorText($row['firstSubject']);
            censorText($row['firstBody']);
            // Don't censor them twice!
            if ($row['ID_FIRST_MSG'] == $row['ID_LAST_MSG']) {
                $row['lastSubject'] = $row['firstSubject'];
                $row['lastBody'] = $row['firstBody'];
            } else {
                censorText($row['lastSubject']);
                censorText($row['lastBody']);
            }
            // Decide how many pages the topic should have.
            $topic_length = $row['numReplies'] + 1;
            if ($topic_length > $modSettings['defaultMaxMessages']) {
                $tmppages = array();
                $tmpa = 1;
                for ($tmpb = 0; $tmpb < $topic_length; $tmpb += $modSettings['defaultMaxMessages']) {
                    $tmppages[] = '<a href="' . $scripturl . '?topic=' . $row['ID_TOPIC'] . '.' . $tmpb . '">' . $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[190] . '</a>';
                }
                $pages .= ' &#187;';
            } else {
                $pages = '';
            }
            // We need to check the topic icons exist...
            if (empty($modSettings['messageIconChecks_disable'])) {
                if (!isset($context['icon_sources'][$row['firstIcon']])) {
                    $context['icon_sources'][$row['firstIcon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['firstIcon'] . '.gif') ? 'images_url' : 'default_images_url';
                }
                if (!isset($context['icon_sources'][$row['lastIcon']])) {
                    $context['icon_sources'][$row['lastIcon']] = file_exists($settings['theme_dir'] . '/images/post/' . $row['lastIcon'] . '.gif') ? 'images_url' : 'default_images_url';
                }
            } else {
                if (!isset($context['icon_sources'][$row['firstIcon']])) {
                    $context['icon_sources'][$row['firstIcon']] = 'images_url';
                }
                if (!isset($context['icon_sources'][$row['lastIcon']])) {
                    $context['icon_sources'][$row['lastIcon']] = '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['firstMemberName'], 'name' => $row['firstDisplayName'], 'id' => $row['firstID_MEMBER'], 'href' => !empty($row['firstID_MEMBER']) ? $scripturl . '?action=profile;u=' . $row['firstID_MEMBER'] : '', 'link' => !empty($row['firstID_MEMBER']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['firstID_MEMBER'] . '" title="' . $txt[92] . ' ' . $row['firstDisplayName'] . '">' . $row['firstDisplayName'] . '</a>' : $row['firstDisplayName']), 'time' => timeformat($row['firstPosterTime']), 'timestamp' => forum_time(true, $row['firstPosterTime']), 'subject' => $row['firstSubject'], 'preview' => $row['firstBody'], 'icon' => $row['firstIcon'], 'icon_url' => $settings[$context['icon_sources'][$row['firstIcon']]] . '/post/' . $row['firstIcon'] . '.gif', 'href' => $scripturl . '?topic=' . $row['ID_TOPIC'] . '.0', 'link' => '<a href="' . $scripturl . '?topic=' . $row['ID_TOPIC'] . '.0">' . $row['firstSubject'] . '</a>'), 'last_post' => array('id' => $row['ID_LAST_MSG'], 'member' => array('username' => $row['lastMemberName'], 'name' => $row['lastDisplayName'], 'id' => $row['lastID_MEMBER'], 'href' => !empty($row['lastID_MEMBER']) ? $scripturl . '?action=profile;u=' . $row['lastID_MEMBER'] : '', 'link' => !empty($row['lastID_MEMBER']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row['lastID_MEMBER'] . '">' . $row['lastDisplayName'] . '</a>' : $row['lastDisplayName']), 'time' => timeformat($row['lastPosterTime']), 'timestamp' => forum_time(true, $row['lastPosterTime']), 'subject' => $row['lastSubject'], 'preview' => $row['lastBody'], 'icon' => $row['lastIcon'], 'icon_url' => $settings[$context['icon_sources'][$row['lastIcon']]] . '/post/' . $row['lastIcon'] . '.gif', 'href' => $scripturl . '?topic=' . $row['ID_TOPIC'] . ($row['numReplies'] == 0 ? '.0' : '.msg' . $row['ID_LAST_MSG']) . '#new', 'link' => '<a href="' . $scripturl . '?topic=' . $row['ID_TOPIC'] . ($row['numReplies'] == 0 ? '.0' : '.msg' . $row['ID_LAST_MSG']) . '#new">' . $row['lastSubject'] . '</a>'), 'tagCount' => 0, 'is_sticky' => !empty($modSettings['enableStickyTopics']) && !empty($row['isSticky']), 'is_locked' => !empty($row['locked']), 'is_poll' => $modSettings['pollMode'] == '1' && $row['ID_POLL'] > 0, 'is_hot' => $row['numReplies'] >= $modSettings['hotTopicPosts'], 'is_very_hot' => $row['numReplies'] >= $modSettings['hotTopicVeryPosts'], 'is_posted_in' => false, 'icon' => $row['firstIcon'], 'icon_url' => $settings[$context['icon_sources'][$row['firstIcon']]] . '/post/' . $row['firstIcon'] . '.gif', 'subject' => $row['firstSubject'], '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', 'pages' => $pages, 'replies' => $row['numReplies'], 'views' => $row['numViews']);
            determineTopicClass($context['topics'][$row['ID_TOPIC']]);
        }
        mysql_free_result($result);
        // Tagging system
        if ($modSettings['smftags_set_display_messageindex'] && in_array($context['current_board'], explode(" ", $modSettings['smftags_set_taggable'])) && !empty($topic_ids)) {
            // this query is seperated due to lack of optimisation if integrated in to the main query above
            $result = db_query('
				SELECT ID_TOPIC, COUNT(ID_TAG) AS tagCount
				FROM smf_tags_log
				WHERE ID_TOPIC IN (' . implode(', ', $topic_ids) . ')
				GROUP BY ID_TOPIC
				', __FILE__, __LINE__);
            while ($row = mysql_fetch_assoc($result)) {
                $context['topics'][$row['ID_TOPIC']]['tagCount'] = !empty($row['tagCount']) ? $row['tagCount'] : 0;
            }
            mysql_free_result($result);
        }
        // End tagging system
        // 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)) {
            $result = db_query("\n\t\t\t\tSELECT ID_TOPIC\n\t\t\t\tFROM {$db_prefix}messages\n\t\t\t\tWHERE ID_TOPIC IN (" . implode(', ', $topic_ids) . ")\n\t\t\t\t\tAND ID_MEMBER = {$ID_MEMBER}\n\t\t\t\tGROUP BY ID_TOPIC\n\t\t\t\tLIMIT " . count($topic_ids), __FILE__, __LINE__);
            while ($row = mysql_fetch_assoc($result)) {
                $context['topics'][$row['ID_TOPIC']]['is_posted_in'] = true;
                $context['topics'][$row['ID_TOPIC']]['class'] = 'my_' . $context['topics'][$row['ID_TOPIC']]['class'];
            }
            mysql_free_result($result);
        }
    }
    loadJumpTo();
    // Is Quick Moderation active?
    if (!empty($options['display_quick_mod'])) {
        $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');
        // Set permissions for all the topics.
        foreach ($context['topics'] as $t => $topic) {
            $started = $topic['first_post']['member']['id'] == $ID_MEMBER;
            $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'));
            $context['can_lock'] |= $started && allowedTo('lock_own');
            $context['can_move'] |= $started && allowedTo('move_own');
            $context['can_remove'] |= $started && allowedTo('remove_own');
        }
        $board_count = 0;
        foreach ($context['jump_to'] as $id => $cat) {
            if (!empty($_SESSION['move_to_topic']) && isset($context['jump_to'][$id]['boards'][$_SESSION['move_to_topic']])) {
                $context['jump_to'][$id]['boards'][$_SESSION['move_to_topic']]['selected'] = true;
            }
            $board_count += count($context['jump_to'][$id]['boards']);
        }
        // You can only see just this one board?
        if ($board_count <= 1) {
            $context['can_move'] = false;
        }
    }
    // 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'];
}
Ejemplo n.º 3
0
function Display()
{
    global $scripturl, $txt, $db_prefix, $modSettings, $context, $settings;
    global $options, $sourcedir, $user_info, $ID_MEMBER, $board_info, $topic;
    global $board, $attachments, $messages_request, $language;
    // What are you gonna display if these are empty?!
    if (empty($topic)) {
        fatal_lang_error('smf232', false);
    }
    // Load the proper template and/or sub template.
    if (WIRELESS) {
        $context['sub_template'] = WIRELESS_PROTOCOL . '_display';
    } else {
        loadTemplate('Display');
    }
    // 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;
    }
    // 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) {
            // Just prepare some variables that are used in the query.
            $gt_lt = $_REQUEST['prev_next'] == 'prev' ? '>' : '<';
            $order = $_REQUEST['prev_next'] == 'prev' ? '' : ' DESC';
            $request = db_query("\n\t\t\t\tSELECT t2.ID_TOPIC\n\t\t\t\tFROM ({$db_prefix}topics AS t, {$db_prefix}topics AS t2)\n\t\t\t\tWHERE t.ID_TOPIC = {$topic}" . (empty($modSettings['enableStickyTopics']) ? "\n\t\t\t\t\tAND t2.ID_LAST_MSG {$gt_lt} t.ID_LAST_MSG" : "\n\t\t\t\t\tAND ((t2.ID_LAST_MSG {$gt_lt} t.ID_LAST_MSG AND t2.isSticky {$gt_lt}= t.isSticky) OR t2.isSticky {$gt_lt} t.isSticky)") . "\n\t\t\t\t\tAND t2.ID_BOARD = {$board}\n\t\t\t\tORDER BY" . (empty($modSettings['enableStickyTopics']) ? '' : " t2.isSticky{$order},") . " t2.ID_LAST_MSG{$order}\n\t\t\t\tLIMIT 1", __FILE__, __LINE__);
            // No more left.
            if (mysql_num_rows($request) == 0) {
                mysql_free_result($request);
                // Roll over - if we're going prev, get the last - otherwise the first.
                $request = db_query("\n\t\t\t\t\tSELECT ID_TOPIC\n\t\t\t\t\tFROM {$db_prefix}topics\n\t\t\t\t\tWHERE ID_BOARD = {$board}\n\t\t\t\t\tORDER BY" . (empty($modSettings['enableStickyTopics']) ? '' : " isSticky{$order},") . " ID_LAST_MSG{$order}\n\t\t\t\t\tLIMIT 1", __FILE__, __LINE__);
            }
            // Now you can be sure $topic is the ID_TOPIC to view.
            list($topic) = mysql_fetch_row($request);
            mysql_free_result($request);
            $context['current_topic'] = $topic;
        }
        // Go to the newest message on this topic.
        $_REQUEST['start'] = 'new';
        // Duplicate link!  Tell the robots not to link this.
        $context['robot_no_index'] = true;
    }
    // Add 1 to the number of views of this topic.
    if (empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic) {
        db_query("\n\t\t\tUPDATE {$db_prefix}topics\n\t\t\tSET numViews = numViews + 1\n\t\t\tWHERE ID_TOPIC = {$topic}\n\t\t\tLIMIT 1", __FILE__, __LINE__);
        $_SESSION['last_read_topic'] = $topic;
    }
    // Get all the important topic info.
    $request = db_query("\n\t\tSELECT\n\t\t\tt.numReplies, t.numViews, t.locked, ms.subject, t.isSticky, t.ID_POLL,\n\t\t\tt.ID_MEMBER_STARTED, t.ID_FIRST_MSG, t.ID_LAST_MSG,\n\t\t\t" . ($user_info['is_guest'] ? '0' : 'IFNULL(lt.ID_MSG, -1) + 1') . " AS new_from\n\t\tFROM ({$db_prefix}topics AS t, {$db_prefix}messages AS ms)" . ($user_info['is_guest'] ? '' : "\n\t\t\tLEFT JOIN {$db_prefix}log_topics AS lt ON (lt.ID_TOPIC = {$topic} AND lt.ID_MEMBER = {$ID_MEMBER})") . "\n\t\tWHERE t.ID_TOPIC = {$topic}\n\t\t\tAND ms.ID_MSG = t.ID_FIRST_MSG\n\t\tLIMIT 1", __FILE__, __LINE__);
    if (mysql_num_rows($request) == 0) {
        fatal_lang_error(472, false);
    }
    $topicinfo = mysql_fetch_assoc($request);
    mysql_free_result($request);
    // 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 topic.
            if ($user_info['is_guest']) {
                $context['start_from'] = $topicinfo['numReplies'];
                $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : 0;
            } else {
                // Find the earliest unread message in the topic. (the use of topics here is just for both tables.)
                $request = db_query("\n\t\t\t\t\tSELECT IFNULL(lt.ID_MSG, IFNULL(lmr.ID_MSG, -1)) + 1 AS new_from\n\t\t\t\t\tFROM {$db_prefix}topics AS t\n\t\t\t\t\t\tLEFT JOIN {$db_prefix}log_topics AS lt ON (lt.ID_TOPIC = {$topic} AND lt.ID_MEMBER = {$ID_MEMBER})\n\t\t\t\t\t\tLEFT JOIN {$db_prefix}log_mark_read AS lmr ON (lmr.ID_BOARD = {$board} AND lmr.ID_MEMBER = {$ID_MEMBER})\n\t\t\t\t\tWHERE t.ID_TOPIC = {$topic}\n\t\t\t\t\tLIMIT 1", __FILE__, __LINE__);
                list($new_from) = mysql_fetch_row($request);
                mysql_free_result($request);
                // Fall through to the next if statement.
                $_REQUEST['start'] = 'msg' . $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...
                $request = db_query("\n\t\t\t\t\tSELECT COUNT(*)\n\t\t\t\t\tFROM {$db_prefix}messages\n\t\t\t\t\tWHERE posterTime < {$timestamp}\n\t\t\t\t\t\tAND ID_TOPIC = {$topic}", __FILE__, __LINE__);
                list($context['start_from']) = mysql_fetch_row($request);
                mysql_free_result($request);
                // Handle view_newest_first options, and get the correct start value.
                $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $topicinfo['numReplies'] - $context['start_from'];
            }
        } elseif (substr($_REQUEST['start'], 0, 3) == 'msg') {
            $virtual_msg = (int) substr($_REQUEST['start'], 3);
            if ($virtual_msg >= $topicinfo['ID_LAST_MSG']) {
                $context['start_from'] = $topicinfo['numReplies'];
            } elseif ($virtual_msg <= $topicinfo['ID_FIRST_MSG']) {
                $context['start_from'] = 0;
            } else {
                // Find the start value for that message......
                $request = db_query("\n\t\t\t\t\tSELECT COUNT(*)\n\t\t\t\t\tFROM {$db_prefix}messages\n\t\t\t\t\tWHERE ID_MSG < {$virtual_msg}\n\t\t\t\t\t\tAND ID_TOPIC = {$topic}", __FILE__, __LINE__);
                list($context['start_from']) = mysql_fetch_row($request);
                mysql_free_result($request);
            }
            // We need to reverse the start as well in this case.
            $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $topicinfo['numReplies'] - $context['start_from'];
            $context['robot_no_index'] = true;
        }
    }
    // Create a previous next string if the selected theme has it as a selected option.
    $context['previous_next'] = $modSettings['enablePreviousNext'] ? '<a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=prev#new">' . $txt['previous_next_back'] . '</a> <a href="' . $scripturl . '?topic=' . $topic . '.0;prev_next=next#new">' . $txt['previous_next_forward'] . '</a>' : '';
    // Check if spellchecking is both enabled and actually working. (for quick reply.)
    $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
    // Censor the title...
    censorText($topicinfo['subject']);
    $context['page_title'] = $topicinfo['subject'];
    $context['num_replies'] = $topicinfo['numReplies'];
    $context['topic_first_message'] = $topicinfo['ID_FIRST_MSG'];
    // Is this topic sticky, or can it even be?
    $topicinfo['isSticky'] = empty($modSettings['enableStickyTopics']) ? '0' : $topicinfo['isSticky'];
    // Default this topic to not marked for notifications... of course...
    $context['is_marked_notify'] = false;
    // Guests can't mark topics read or for notifications, just can't sorry.
    if (!$user_info['is_guest']) {
        // Mark the topic as read :)
        if (!empty($topicinfo['new_from'])) {
            db_query("\n\t\t\t\tUPDATE {$db_prefix}log_topics\n\t\t\t\tSET ID_MSG = {$modSettings['maxMsgID']}\n\t\t\t\tWHERE ID_MEMBER = {$ID_MEMBER}\n\t\t\t\t\tAND ID_TOPIC = {$topic}\n\t\t\t\tLIMIT 1", __FILE__, __LINE__);
            $flag = db_affected_rows() !== 0;
        }
        if (empty($flag)) {
            db_query("\n\t\t\t\tREPLACE INTO {$db_prefix}log_topics\n\t\t\t\t\t(ID_MSG, ID_MEMBER, ID_TOPIC)\n\t\t\t\tVALUES ({$modSettings['maxMsgID']}, {$ID_MEMBER}, {$topic})", __FILE__, __LINE__);
        }
        // Check for notifications on this topic OR board.
        $request = db_query("\n\t\t\tSELECT sent, ID_TOPIC\n\t\t\tFROM {$db_prefix}log_notify\n\t\t\tWHERE (ID_TOPIC = {$topic} OR ID_BOARD = {$board})\n\t\t\t\tAND ID_MEMBER = {$ID_MEMBER}\n\t\t\tLIMIT 2", __FILE__, __LINE__);
        $do_once = true;
        while ($row = mysql_fetch_assoc($request)) {
            // Find if this topic is marked for notification...
            if (!empty($row['ID_TOPIC'])) {
                $context['is_marked_notify'] = true;
            }
            // Only do this once, but mark the notifications as "not sent yet" for next time.
            if (!empty($row['sent']) && $do_once) {
                db_query("\n\t\t\t\t\tUPDATE {$db_prefix}log_notify\n\t\t\t\t\tSET sent = 0\n\t\t\t\t\tWHERE (ID_TOPIC = {$topic} OR ID_BOARD = {$board})\n\t\t\t\t\t\tAND ID_MEMBER = {$ID_MEMBER}\n\t\t\t\t\tLIMIT 2", __FILE__, __LINE__);
                $do_once = false;
            }
        }
        // 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.
            $request = db_query("\n\t\t\t\tSELECT COUNT(*)\n\t\t\t\tFROM {$db_prefix}topics AS t\n\t\t\t\t\tLEFT JOIN {$db_prefix}log_boards AS lb ON (lb.ID_BOARD = {$board} AND lb.ID_MEMBER = {$ID_MEMBER})\n\t\t\t\t\tLEFT JOIN {$db_prefix}log_topics AS lt ON (lt.ID_TOPIC = t.ID_TOPIC AND lt.ID_MEMBER = {$ID_MEMBER})\n\t\t\t\tWHERE t.ID_BOARD = {$board}\n\t\t\t\t\tAND t.ID_LAST_MSG > IFNULL(lb.ID_MSG, 0)\n\t\t\t\t\tAND t.ID_LAST_MSG > IFNULL(lt.ID_MSG, 0)" . (empty($_SESSION['ID_MSG_LAST_VISIT']) ? '' : "\n\t\t\t\t\tAND t.ID_LAST_MSG > {$_SESSION['ID_MSG_LAST_VISIT']}"), __FILE__, __LINE__);
            list($numNewTopics) = mysql_fetch_row($request);
            mysql_free_result($request);
            // 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'])) {
            db_query("\n\t\t\t\tREPLACE INTO {$db_prefix}log_boards\n\t\t\t\t\t(ID_MSG, ID_MEMBER, ID_BOARD)\n\t\t\t\tVALUES ({$modSettings['maxMsgID']}, {$ID_MEMBER}, {$board})", __FILE__, __LINE__);
        }
    }
    // Let's get nosey, who is viewing this topic?
    if (!empty($settings['display_who_viewing'])) {
        // Start out with no one at all viewing it.
        $context['view_members'] = array();
        $context['view_members_list'] = array();
        $context['view_num_hidden'] = 0;
        // Search for members who have this topic set in their GET data.
        $request = db_query("\n\t\t\tSELECT\n\t\t\t\tlo.ID_MEMBER, lo.logTime, mem.realName, mem.memberName, mem.showOnline,\n\t\t\t\tmg.onlineColor, mg.ID_GROUP, mg.groupName\n\t\t\tFROM {$db_prefix}log_online AS lo\n\t\t\t\tLEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = lo.ID_MEMBER)\n\t\t\t\tLEFT JOIN {$db_prefix}membergroups AS mg ON (mg.ID_GROUP = IF(mem.ID_GROUP = 0, mem.ID_POST_GROUP, mem.ID_GROUP))\n\t\t\tWHERE INSTR(lo.url, 's:5:\"topic\";i:{$topic};') OR lo.session = '" . ($user_info['is_guest'] ? 'ip' . $user_info['ip'] : session_id()) . "'", __FILE__, __LINE__);
        while ($row = mysql_fetch_assoc($request)) {
            if (empty($row['ID_MEMBER'])) {
                continue;
            }
            if (!empty($row['onlineColor'])) {
                $link = '<a href="' . $scripturl . '?action=profile;u=' . $row['ID_MEMBER'] . '" style="color: ' . $row['onlineColor'] . ';">' . $row['realName'] . '</a>';
            } else {
                $link = '<a href="' . $scripturl . '?action=profile;u=' . $row['ID_MEMBER'] . '">' . $row['realName'] . '</a>';
            }
            $is_buddy = in_array($row['ID_MEMBER'], $user_info['buddies']);
            if ($is_buddy) {
                $link = '<b>' . $link . '</b>';
            }
            // Add them both to the list and to the more detailed list.
            if (!empty($row['showOnline']) || allowedTo('moderate_forum')) {
                $context['view_members_list'][$row['logTime'] . $row['memberName']] = empty($row['showOnline']) ? '<i>' . $link . '</i>' : $link;
            }
            $context['view_members'][$row['logTime'] . $row['memberName']] = array('id' => $row['ID_MEMBER'], 'username' => $row['memberName'], 'name' => $row['realName'], 'group' => $row['ID_GROUP'], 'href' => $scripturl . '?action=profile;u=' . $row['ID_MEMBER'], 'link' => $link, 'is_buddy' => $is_buddy, 'hidden' => empty($row['showOnline']));
            if (empty($row['showOnline'])) {
                $context['view_num_hidden']++;
            }
        }
        // The number of guests is equal to the rows minus the ones we actually used ;).
        $context['view_num_guests'] = mysql_num_rows($request) - count($context['view_members']);
        mysql_free_result($request);
        // Sort the list.
        krsort($context['view_members']);
        krsort($context['view_members_list']);
    }
    // If all is set, but not allowed... just unset it.
    if (isset($_REQUEST['all']) && empty($modSettings['enableAllMessages'])) {
        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 . '.%d', $_REQUEST['start'], $topicinfo['numReplies'] + 1, $modSettings['defaultMaxMessages'], true);
    $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'] / $modSettings['defaultMaxMessages'] + 1, 'num_pages' => floor($topicinfo['numReplies'] / $modSettings['defaultMaxMessages']) + 1);
    // Figure out all the link to the next/prev/first/last/etc. for wireless mainly.
    $context['links'] = array('first' => $_REQUEST['start'] >= $modSettings['defaultMaxMessages'] ? $scripturl . '?topic=' . $topic . '.0' : '', 'prev' => $_REQUEST['start'] >= $modSettings['defaultMaxMessages'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] - $modSettings['defaultMaxMessages']) : '', 'next' => $_REQUEST['start'] + $modSettings['defaultMaxMessages'] < $topicinfo['numReplies'] + 1 ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] + $modSettings['defaultMaxMessages']) : '', 'last' => $_REQUEST['start'] + $modSettings['defaultMaxMessages'] < $topicinfo['numReplies'] + 1 ? $scripturl . '?topic=' . $topic . '.' . floor($topicinfo['numReplies'] / $modSettings['defaultMaxMessages']) * $modSettings['defaultMaxMessages'] : '', 'up' => $scripturl . '?board=' . $board . '.0');
    // If they are viewing all the posts, show all the posts, otherwise limit the number.
    if (!empty($modSettings['enableAllMessages']) && $topicinfo['numReplies'] + 1 > $modSettings['defaultMaxMessages'] && $topicinfo['numReplies'] + 1 < $modSettings['enableAllMessages']) {
        if (isset($_REQUEST['all'])) {
            // No limit! (actually, there is a limit, but...)
            $modSettings['defaultMaxMessages'] = -1;
            $context['page_index'] .= empty($modSettings['compactTopicPagesEnable']) ? '<b>' . $txt[190] . '</b> ' : '[<b>' . $txt[190] . '</b>] ';
            // Set start back to 0...
            $_REQUEST['start'] = 0;
        } else {
            $context['page_index'] .= '&nbsp;<a href="' . $scripturl . '?topic=' . $topic . '.0;all">' . $txt[190] . '</a> ';
        }
    }
    // Build the link tree.
    $context['linktree'][] = array('url' => $scripturl . '?topic=' . $topic . '.0', 'name' => $topicinfo['subject'], 'extra_before' => $settings['linktree_inline'] ? $txt[118] . ': ' : '');
    // Build a list of this board's moderators.
    $context['moderators'] =& $board_info['moderators'];
    $context['link_moderators'] = array();
    if (!empty($board_info['moderators'])) {
        // Add a link for each moderator...
        foreach ($board_info['moderators'] as $mod) {
            $context['link_moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $mod['id'] . '" title="' . $txt[62] . '">' . $mod['name'] . '</a>';
        }
        // And show it after the board's name.
        $context['linktree'][count($context['linktree']) - 2]['extra_after'] = ' (' . (count($context['link_moderators']) == 1 ? $txt[298] : $txt[299]) . ': ' . implode(', ', $context['link_moderators']) . ')';
    }
    // Information about the current topic...
    $context['is_locked'] = $topicinfo['locked'];
    $context['is_sticky'] = $topicinfo['isSticky'];
    $context['is_very_hot'] = $topicinfo['numReplies'] >= $modSettings['hotTopicVeryPosts'];
    $context['is_hot'] = $topicinfo['numReplies'] >= $modSettings['hotTopicPosts'];
    // We don't want to show the poll icon in the topic class here, so pretend it's not one.
    $context['is_poll'] = false;
    determineTopicClass($context);
    $context['is_poll'] = $topicinfo['ID_POLL'] > 0 && $modSettings['pollMode'] == '1' && allowedTo('poll_view');
    // Did this user start the topic or not?
    $context['user']['started'] = $ID_MEMBER == $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['numViews'];
    $context['mark_unread_time'] = $topicinfo['new_from'];
    // For quick reply we need a response prefix in the default forum language.
    if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix'))) {
        if ($language === $user_info['language']) {
            $context['response_prefix'] = $txt['response_prefix'];
        } else {
            loadLanguage('index', $language, false);
            $context['response_prefix'] = $txt['response_prefix'];
            loadLanguage('index');
        }
        cache_put_data('response_prefix', $context['response_prefix'], 600);
    }
    // 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'])) {
        // 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];
        }
        // Any calendar information for this topic?
        $request = db_query("\n\t\t\tSELECT cal.ID_EVENT, cal.startDate, cal.endDate, cal.title, cal.ID_MEMBER, mem.realName\n\t\t\tFROM {$db_prefix}calendar AS cal\n\t\t\t\tLEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = cal.ID_MEMBER)\n\t\t\tWHERE cal.ID_TOPIC = {$topic}\n\t\t\tORDER BY startDate", __FILE__, __LINE__);
        $context['linked_calendar_events'] = array();
        while ($row = mysql_fetch_assoc($request)) {
            // Prepare the dates for being formatted.
            $startDate = sscanf($row['startDate'], '%04d-%02d-%02d');
            $startDate = mktime(12, 0, 0, $startDate[1], $startDate[2], $startDate[0]);
            $endDate = sscanf($row['endDate'], '%04d-%02d-%02d');
            $endDate = mktime(12, 0, 0, $endDate[1], $endDate[2], $endDate[0]);
            $context['linked_calendar_events'][] = array('id' => $row['ID_EVENT'], 'title' => $row['title'], 'can_edit' => allowedTo('calendar_edit_any') || $row['ID_MEMBER'] == $ID_MEMBER && allowedTo('calendar_edit_own'), 'modify_href' => $scripturl . '?action=post;msg=' . $topicinfo['ID_FIRST_MSG'] . ';topic=' . $topic . '.0;calendar;eventid=' . $row['ID_EVENT'] . ';sesc=' . $context['session_id'], 'start_date' => timeformat($startDate, $date_string), 'start_timestamp' => forum_time(true, $startDate), 'end_date' => timeformat($endDate, $date_string), 'end_timestamp' => forum_time(true, $startDate), 'is_last' => false);
        }
        mysql_free_result($request);
        if (!empty($context['linked_calendar_events'])) {
            $context['linked_calendar_events'][count($context['linked_calendar_events']) - 1]['is_last'] = true;
        }
    }
    // Create the poll info if it exists.
    if ($context['is_poll']) {
        // Get the question and if it's locked.
        $request = db_query("\n\t\t\tSELECT\n\t\t\t\tp.question, p.votingLocked, p.hideResults, p.expireTime, p.maxVotes, p.changeVote,\n\t\t\t\tp.ID_MEMBER, IFNULL(mem.realName, p.posterName) AS posterName,\n\t\t\t\tCOUNT(DISTINCT lp.ID_MEMBER) AS total\n\t\t\tFROM {$db_prefix}polls AS p\n\t\t\t\tLEFT JOIN {$db_prefix}log_polls AS lp ON (lp.ID_POLL = p.ID_POLL)\n\t\t\t\tLEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = p.ID_MEMBER)\n\t\t\tWHERE p.ID_POLL = {$topicinfo['ID_POLL']}\n\t\t\tGROUP BY p.ID_POLL\n\t\t\tLIMIT 1", __FILE__, __LINE__);
        $pollinfo = mysql_fetch_assoc($request);
        mysql_free_result($request);
        // Get all the options, and calculate the total votes.
        $request = db_query("\n\t\t\tSELECT pc.ID_CHOICE, pc.label, pc.votes, IFNULL(lp.ID_CHOICE, -1) AS votedThis\n\t\t\tFROM {$db_prefix}poll_choices AS pc\n\t\t\t\tLEFT JOIN {$db_prefix}log_polls AS lp ON (lp.ID_CHOICE = pc.ID_CHOICE AND lp.ID_POLL = {$topicinfo['ID_POLL']} AND lp.ID_MEMBER = {$ID_MEMBER})\n\t\t\tWHERE pc.ID_POLL = {$topicinfo['ID_POLL']}", __FILE__, __LINE__);
        $pollOptions = array();
        $realtotal = 0;
        $pollinfo['has_voted'] = false;
        while ($row = mysql_fetch_assoc($request)) {
            censorText($row['label']);
            $pollOptions[$row['ID_CHOICE']] = $row;
            $realtotal += $row['votes'];
            $pollinfo['has_voted'] |= $row['votedThis'] != -1;
        }
        mysql_free_result($request);
        // Set up the basic poll information.
        $context['poll'] = array('id' => $topicinfo['ID_POLL'], 'image' => 'normal_' . (empty($pollinfo['votingLocked']) ? 'poll' : 'locked_poll'), 'question' => parse_bbc($pollinfo['question']), 'total_votes' => $pollinfo['total'], 'change_vote' => !empty($pollinfo['changeVote']), 'is_locked' => !empty($pollinfo['votingLocked']), 'options' => array(), 'lock' => allowedTo('poll_lock_any') || $context['user']['started'] && allowedTo('poll_lock_own'), 'edit' => allowedTo('poll_edit_any') || $context['user']['started'] && allowedTo('poll_edit_own'), 'allowed_warning' => $pollinfo['maxVotes'] > 1 ? sprintf($txt['poll_options6'], $pollinfo['maxVotes']) : '', 'is_expired' => !empty($pollinfo['expireTime']) && $pollinfo['expireTime'] < time(), 'expire_time' => !empty($pollinfo['expireTime']) ? timeformat($pollinfo['expireTime']) : 0, 'has_voted' => !empty($pollinfo['has_voted']), 'starter' => array('id' => $pollinfo['ID_MEMBER'], 'name' => $row['posterName'], 'href' => $pollinfo['ID_MEMBER'] == 0 ? '' : $scripturl . '?action=profile;u=' . $pollinfo['ID_MEMBER'], 'link' => $pollinfo['ID_MEMBER'] == 0 ? $row['posterName'] : '<a href="' . $scripturl . '?action=profile;u=' . $pollinfo['ID_MEMBER'] . '">' . $row['posterName'] . '</a>'));
        // You're allowed to vote if:
        // 1. the poll did not expire, and
        // 2. you're not a guest... and
        // 3. you're not trying to view the results, and
        // 4. the poll is not locked, and
        // 5. you have the proper permissions, and
        // 6. you haven't already voted before.
        $context['allow_vote'] = !$context['poll']['is_expired'] && !$user_info['is_guest'] && empty($pollinfo['votingLocked']) && allowedTo('poll_vote') && !$context['poll']['has_voted'];
        // You're allowed to view the results if:
        // 1. you're just a super-nice-guy, or
        // 2. anyone can see them (hideResults == 0), or
        // 3. you can see them after you voted (hideResults == 1), or
        // 4. you've waited long enough for the poll to expire. (whether hideResults is 1 or 2.)
        $context['allow_poll_view'] = allowedTo('moderate_board') || $pollinfo['hideResults'] == 0 || $pollinfo['hideResults'] == 1 && $context['poll']['has_voted'] || $context['poll']['is_expired'];
        $context['poll']['show_results'] = $context['allow_poll_view'] && isset($_REQUEST['viewResults']);
        // You're allowed to change your vote if:
        // 1. the poll did not expire, and
        // 2. you're not a guest... and
        // 3. the poll is not locked, and
        // 4. you have the proper permissions, and
        // 5. you have already voted, and
        // 6. the poll creator has said you can!
        $context['allow_change_vote'] = !$context['poll']['is_expired'] && !$user_info['is_guest'] && empty($pollinfo['votingLocked']) && allowedTo('poll_vote') && $context['poll']['has_voted'] && $context['poll']['change_vote'];
        // Calculate the percentages and bar lengths...
        $divisor = $realtotal == 0 ? 1 : $realtotal;
        // Determine if a decimal point is needed in order for the options to add to 100%.
        $precision = $realtotal == 100 ? 0 : 1;
        // Now look through each option, and...
        foreach ($pollOptions as $i => $option) {
            // First calculate the percentage, and then the width of the bar...
            $bar = round($option['votes'] * 100 / $divisor, $precision);
            $barWide = $bar == 0 ? 1 : floor($bar * 8 / 3);
            // Now add it to the poll's contextual theme data.
            $context['poll']['options'][$i] = array('id' => 'options-' . $i, 'percent' => $bar, 'votes' => $option['votes'], 'voted_this' => $option['votedThis'] != -1, 'bar' => '<span style="white-space: nowrap;"><img src="' . $settings['images_url'] . '/poll_left.gif" alt="" /><img src="' . $settings['images_url'] . '/poll_middle.gif" width="' . $barWide . '" height="12" alt="-" /><img src="' . $settings['images_url'] . '/poll_right.gif" alt="" /></span>', 'bar_width' => $barWide, 'option' => parse_bbc($option['label']), 'vote_button' => '<input type="' . ($pollinfo['maxVotes'] > 1 ? 'checkbox' : 'radio') . '" name="options[]" id="options-' . $i . '" value="' . $i . '" class="check" />');
        }
    }
    // Calculate the fastest way to get the messages!
    $ascending = empty($options['view_newest_first']);
    $start = $_REQUEST['start'];
    $limit = $modSettings['defaultMaxMessages'];
    $firstIndex = 0;
    if ($start > $topicinfo['numReplies'] / 2 && $modSettings['defaultMaxMessages'] != -1) {
        $ascending = !$ascending;
        $limit = $topicinfo['numReplies'] < $start + $limit ? $topicinfo['numReplies'] - $start + 1 : $limit;
        $start = $topicinfo['numReplies'] < $start + $limit ? 0 : $topicinfo['numReplies'] - $start - $limit + 1;
        $firstIndex = $limit - 1;
    }
    // Get each post and poster in this topic.
    $request = db_query("\n\t\tSELECT ID_MSG, ID_MEMBER\n\t\tFROM {$db_prefix}messages\n\t\tWHERE ID_TOPIC = {$topic}\n\t\tORDER BY ID_MSG " . ($ascending ? '' : 'DESC') . ($modSettings['defaultMaxMessages'] == -1 ? '' : "\n\t\tLIMIT {$start}, {$limit}"), __FILE__, __LINE__);
    $messages = array();
    $posters = array();
    while ($row = mysql_fetch_assoc($request)) {
        if (!empty($row['ID_MEMBER'])) {
            $posters[] = $row['ID_MEMBER'];
        }
        $messages[] = $row['ID_MSG'];
    }
    mysql_free_result($request);
    $posters = array_unique($posters);
    $attachments = array();
    // If there _are_ messages here... (probably an error otherwise :!)
    if (!empty($messages)) {
        // Fetch attachments.
        if (!empty($modSettings['attachmentEnable']) && allowedTo('view_attachments')) {
            $request = db_query("\n\t\t\t\tSELECT\n\t\t\t\t\ta.ID_ATTACH, a.ID_MSG, a.filename, IFNULL(a.size, 0) AS filesize, a.downloads,\n\t\t\t\t\ta.width, a.height" . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ",\n\t\t\t\t\tIFNULL(thumb.ID_ATTACH, 0) AS ID_THUMB, thumb.width AS thumb_width, thumb.height AS thumb_height") . "\n\t\t\t\tFROM {$db_prefix}attachments AS a" . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : "\n\t\t\t\t\tLEFT JOIN {$db_prefix}attachments AS thumb ON (thumb.ID_ATTACH = a.ID_THUMB)") . "\n\t\t\t\tWHERE a.ID_MSG IN (" . implode(',', $messages) . ")\n\t\t\t\t\tAND a.attachmentType = 0", __FILE__, __LINE__);
            $temp = array();
            while ($row = mysql_fetch_assoc($request)) {
                $temp[$row['ID_ATTACH']] = $row;
                if (!isset($attachments[$row['ID_MSG']])) {
                    $attachments[$row['ID_MSG']] = array();
                }
            }
            mysql_free_result($request);
            // This is better than sorting it with the query...
            ksort($temp);
            foreach ($temp as $row) {
                $attachments[$row['ID_MSG']][] = $row;
            }
        }
        // What?  It's not like it *couldn't* be only guests in this topic...
        if (!empty($posters)) {
            loadMemberData($posters);
        }
        $messages_request = db_query("\n\t\t\tSELECT\n\t\t\t\tID_MSG, icon, subject, posterTime, posterIP, ID_MEMBER, modifiedTime, modifiedName, body,\n\t\t\t\tsmileysEnabled, posterName, posterEmail,\n\t\t\t\tID_MSG_MODIFIED < {$topicinfo['new_from']} AS isRead\n\t\t\tFROM {$db_prefix}messages\n\t\t\tWHERE ID_MSG IN (" . implode(',', $messages) . ")\n\t\t\tORDER BY ID_MSG" . (empty($options['view_newest_first']) ? '' : ' DESC'), __FILE__, __LINE__);
        // 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['numReplies']) {
            $context['start_from'] = $topicinfo['numReplies'];
        }
        // 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];
        if (empty($options['view_newest_first'])) {
            $context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $context['start_from'];
        } else {
            $context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $topicinfo['numReplies'] - $context['start_from'];
        }
    } else {
        $messages_request = false;
        $context['first_message'] = 0;
        $context['first_new_message'] = false;
    }
    // Load the "Jump to" list...
    loadJumpTo();
    // Set the callback.  (do you REALIZE how much memory all the messages would take?!?)
    $context['get_message'] = 'prepareDisplayContext';
    // Basic settings.... may be converted over at some point.
    $context['allow_hide_email'] = !empty($modSettings['allow_hideEmail']) || $user_info['is_guest'] && !empty($modSettings['guest_hideContacts']);
    // Now set all the wonderful, wonderful permissions... like moderation ones...
    $common_permissions = array('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_report_moderator' => 'report_any', 'can_moderate_forum' => 'moderate_forum');
    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');
    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']);
    $context['can_add_poll'] &= $modSettings['pollMode'] == '1' && $topicinfo['ID_POLL'] <= 0;
    $context['can_remove_poll'] &= $modSettings['pollMode'] == '1' && $topicinfo['ID_POLL'] > 0;
    $context['can_reply'] &= empty($topicinfo['locked']) || allowedTo('moderate_board');
    $board_count = 0;
    foreach ($context['jump_to'] as $id => $cat) {
        $board_count += count($context['jump_to'][$id]['boards']);
    }
    $context['can_move'] &= $board_count > 1;
    // 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'];
    // Load up the "double post" sequencing magic.
    if (!empty($options['display_quick_reply'])) {
        checkSubmitOnce('register');
    }
}