Beispiel #1
0
function ReportToModerator2()
{
    global $txt, $scripturl, $topic, $board, $user_info, $modSettings, $sourcedir, $language, $context, $smcFunc;
    // You must have the proper permissions!
    isAllowedTo('report_any');
    // Make sure they aren't spamming.
    spamProtection('reporttm');
    require_once $sourcedir . '/Subs-Post.php';
    // No errors, yet.
    $post_errors = array();
    // Check their session.
    if (checkSession('post', '', false) != '') {
        $post_errors[] = 'session_timeout';
    }
    // Make sure we have a comment and it's clean.
    if (!isset($_POST['comment']) || $smcFunc['htmltrim']($_POST['comment']) === '') {
        $post_errors[] = 'no_comment';
    }
    $poster_comment = strtr($smcFunc['htmlspecialchars']($_POST['comment']), array("\r" => '', "\n" => '', "\t" => ''));
    // Guests need to provide their address!
    if ($user_info['is_guest']) {
        $_POST['email'] = !isset($_POST['email']) ? '' : trim($_POST['email']);
        if ($_POST['email'] === '') {
            $post_errors[] = 'no_email';
        } elseif (preg_match('~^[0-9A-Za-z=_+\\-/][0-9A-Za-z=_\'+\\-/\\.]*@[\\w\\-]+(\\.[\\w\\-]+)*(\\.[\\w]{2,6})$~', $_POST['email']) == 0) {
            $post_errors[] = 'bad_email';
        }
        isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
        $user_info['email'] = htmlspecialchars($_POST['email']);
    }
    // Could they get the right verification code?
    if ($user_info['is_guest'] && !empty($modSettings['guests_report_require_captcha'])) {
        require_once $sourcedir . '/Subs-Editor.php';
        $verificationOptions = array('id' => 'report');
        $context['require_verification'] = create_control_verification($verificationOptions, true);
        if (is_array($context['require_verification'])) {
            $post_errors = array_merge($post_errors, $context['require_verification']);
        }
    }
    // Any errors?
    if (!empty($post_errors)) {
        loadLanguage('Errors');
        $context['post_errors'] = array();
        foreach ($post_errors as $post_error) {
            $context['post_errors'][] = $txt['error_' . $post_error];
        }
        return ReportToModerator();
    }
    // Get the basic topic information, and make sure they can see it.
    $_POST['msg'] = (int) $_POST['msg'];
    $request = $smcFunc['db_query']('', '
		SELECT m.id_topic, m.id_board, m.subject, m.body, m.id_member AS id_poster, m.poster_name, mem.real_name
		FROM {db_prefix}messages AS m
			LEFT JOIN {db_prefix}members AS mem ON (m.id_member = mem.id_member)
		WHERE m.id_msg = {int:id_msg}
			AND m.id_topic = {int:current_topic}
		LIMIT 1', array('current_topic' => $topic, 'id_msg' => $_POST['msg']));
    if ($smcFunc['db_num_rows']($request) == 0) {
        fatal_lang_error('no_board', false);
    }
    $message = $smcFunc['db_fetch_assoc']($request);
    $smcFunc['db_free_result']($request);
    $poster_name = un_htmlspecialchars($message['real_name']) . ($message['real_name'] != $message['poster_name'] ? ' (' . $message['poster_name'] . ')' : '');
    $reporterName = un_htmlspecialchars($user_info['name']) . ($user_info['name'] != $user_info['username'] && $user_info['username'] != '' ? ' (' . $user_info['username'] . ')' : '');
    $subject = un_htmlspecialchars($message['subject']);
    // Get a list of members with the moderate_board permission.
    require_once $sourcedir . '/Subs-Members.php';
    $moderators = membersAllowedTo('moderate_board', $board);
    $request = $smcFunc['db_query']('', '
		SELECT id_member, email_address, lngfile, mod_prefs
		FROM {db_prefix}members
		WHERE id_member IN ({array_int:moderator_list})
			AND notify_types != {int:notify_types}
		ORDER BY lngfile', array('moderator_list' => $moderators, 'notify_types' => 4));
    // Check that moderators do exist!
    if ($smcFunc['db_num_rows']($request) == 0) {
        fatal_lang_error('no_mods', false);
    }
    // If we get here, I believe we should make a record of this, for historical significance, yabber.
    if (empty($modSettings['disable_log_report'])) {
        $request2 = $smcFunc['db_query']('', '
			SELECT id_report, ignore_all
			FROM {db_prefix}log_reported
			WHERE id_msg = {int:id_msg}
				AND (closed = {int:not_closed} OR ignore_all = {int:ignored})
			ORDER BY ignore_all DESC', array('id_msg' => $_POST['msg'], 'not_closed' => 0, 'ignored' => 1));
        if ($smcFunc['db_num_rows']($request2) != 0) {
            list($id_report, $ignore) = $smcFunc['db_fetch_row']($request2);
        }
        $smcFunc['db_free_result']($request2);
        // If we're just going to ignore these, then who gives a monkeys...
        if (!empty($ignore)) {
            redirectexit('topic=' . $topic . '.msg' . $_POST['msg'] . '#msg' . $_POST['msg']);
        }
        // Already reported? My god, we could be dealing with a real rogue here...
        if (!empty($id_report)) {
            $smcFunc['db_query']('', '
				UPDATE {db_prefix}log_reported
				SET num_reports = num_reports + 1, time_updated = {int:current_time}
				WHERE id_report = {int:id_report}', array('current_time' => time(), 'id_report' => $id_report));
        } else {
            if (empty($message['real_name'])) {
                $message['real_name'] = $message['poster_name'];
            }
            $smcFunc['db_insert']('', '{db_prefix}log_reported', array('id_msg' => 'int', 'id_topic' => 'int', 'id_board' => 'int', 'id_member' => 'int', 'membername' => 'string', 'subject' => 'string', 'body' => 'string', 'time_started' => 'int', 'time_updated' => 'int', 'num_reports' => 'int', 'closed' => 'int'), array($_POST['msg'], $message['id_topic'], $message['id_board'], $message['id_poster'], $message['real_name'], $message['subject'], $message['body'], time(), time(), 1, 0), array('id_report'));
            $id_report = $smcFunc['db_insert_id']('{db_prefix}log_reported', 'id_report');
        }
        // Now just add our report...
        if ($id_report) {
            $smcFunc['db_insert']('', '{db_prefix}log_reported_comments', array('id_report' => 'int', 'id_member' => 'int', 'membername' => 'string', 'email_address' => 'string', 'member_ip' => 'string', 'comment' => 'string', 'time_sent' => 'int'), array($id_report, $user_info['id'], $user_info['name'], $user_info['email'], $user_info['ip'], $poster_comment, time()), array('id_comment'));
        }
    }
    // Find out who the real moderators are - for mod preferences.
    $request2 = $smcFunc['db_query']('', '
		SELECT id_member
		FROM {db_prefix}moderators
		WHERE id_board = {int:current_board}', array('current_board' => $board));
    $real_mods = array();
    while ($row = $smcFunc['db_fetch_assoc']($request2)) {
        $real_mods[] = $row['id_member'];
    }
    $smcFunc['db_free_result']($request2);
    // Send every moderator an email.
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        // Maybe they don't want to know?!
        if (!empty($row['mod_prefs'])) {
            list(, , $pref_binary) = explode('|', $row['mod_prefs']);
            if (!($pref_binary & 1) && (!($pref_binary & 2) || !in_array($row['id_member'], $real_mods))) {
                continue;
            }
        }
        $replacements = array('TOPICSUBJECT' => $subject, 'POSTERNAME' => $poster_name, 'REPORTERNAME' => $reporterName, 'TOPICLINK' => $scripturl . '?topic=' . $topic . '.msg' . $_POST['msg'] . '#msg' . $_POST['msg'], 'REPORTLINK' => !empty($id_report) ? $scripturl . '?action=moderate;area=reports;report=' . $id_report : '', 'COMMENT' => $_POST['comment']);
        $emaildata = loadEmailTemplate('report_to_moderator', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);
        // Send it to the moderator.
        sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], $user_info['email'], null, false, 2);
    }
    $smcFunc['db_free_result']($request);
    // Keep track of when the mod reports get updated, that way we know when we need to look again.
    updateSettings(array('last_mod_report_action' => time()));
    // Back to the post we reported!
    redirectexit('reportsent;topic=' . $topic . '.msg' . $_POST['msg'] . '#msg' . $_POST['msg']);
}
Beispiel #2
0
function Register2($verifiedOpenID = false)
{
    global $txt, $modSettings, $context, $sourcedir;
    // Start collecting together any errors.
    $reg_errors = array();
    // Did we save some open ID fields?
    if ($verifiedOpenID && !empty($context['openid_save_fields'])) {
        foreach ($context['openid_save_fields'] as $id => $value) {
            $_POST[$id] = $value;
        }
    }
    // You can't register if it's disabled.
    if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 3) {
        fatal_lang_error('registration_disabled', false);
    }
    // Things we don't do for people who have already confirmed their OpenID allegances via register.
    if (!$verifiedOpenID) {
        // Well, if you don't agree, you can't register.
        if (!empty($modSettings['requireAgreement']) && empty($_SESSION['registration_agreed'])) {
            redirectexit();
        }
        // Make sure they came from *somewhere*, have a session.
        if (!isset($_SESSION['old_url'])) {
            redirectexit('action=register');
        }
        // Are they under age, and under age users are banned?
        if (!empty($modSettings['coppaAge']) && empty($modSettings['coppaType']) && empty($_SESSION['skip_coppa'])) {
            // !!! This should be put in Errors, imho.
            loadLanguage('Login');
            fatal_lang_error('under_age_registration_prohibited', false, array($modSettings['coppaAge']));
        }
        // Check whether the visual verification code was entered correctly.
        if (!empty($modSettings['reg_verification'])) {
            require_once $sourcedir . '/lib/Subs-Editor.php';
            $verificationOptions = array('id' => 'register');
            $context['visual_verification'] = create_control_verification($verificationOptions, true);
            if (is_array($context['visual_verification'])) {
                loadLanguage('Errors');
                foreach ($context['visual_verification'] as $error) {
                    $reg_errors[] = $txt['error_' . $error];
                }
            }
        }
    }
    foreach ($_POST as $key => $value) {
        if (!is_array($_POST[$key])) {
            $_POST[$key] = htmltrim__recursive(str_replace(array("\n", "\r"), '', $_POST[$key]));
        }
    }
    // Collect all extra registration fields someone might have filled in.
    $possible_strings = array('location', 'birthdate', 'time_format', 'buddy_list', 'pm_ignore_list', 'smiley_set', 'signature', 'personal_text', 'avatar', 'lngfile', 'secret_question', 'secret_answer');
    $possible_ints = array('pm_email_notify', 'notify_types', 'gender', 'id_theme');
    $possible_floats = array('time_offset');
    $possible_bools = array('notify_announcements', 'notify_regularity', 'notify_send_body', 'hide_email', 'show_online');
    if (isset($_POST['secret_answer']) && $_POST['secret_answer'] != '') {
        $_POST['secret_answer'] = md5($_POST['secret_answer']);
    }
    // Needed for isReservedName() and registerMember().
    require_once $sourcedir . '/lib/Subs-Members.php';
    // Validation... even if we're not a mall.
    if (isset($_POST['real_name']) && (!empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum'))) {
        $_POST['real_name'] = trim(preg_replace('~[\\s]~u', ' ', $_POST['real_name']));
        if (trim($_POST['real_name']) != '' && !isReservedName($_POST['real_name']) && commonAPI::strlen($_POST['real_name']) < 60) {
            $possible_strings[] = 'real_name';
        }
    }
    // Handle a string as a birthdate...
    if (isset($_POST['birthdate']) && $_POST['birthdate'] != '') {
        $_POST['birthdate'] = strftime('%Y-%m-%d', strtotime($_POST['birthdate']));
    } elseif (!empty($_POST['bday1']) && !empty($_POST['bday2'])) {
        $_POST['birthdate'] = sprintf('%04d-%02d-%02d', empty($_POST['bday3']) ? 0 : (int) $_POST['bday3'], (int) $_POST['bday1'], (int) $_POST['bday2']);
    }
    // By default assume email is hidden, only show it if we tell it to.
    $_POST['hide_email'] = !empty($_POST['allow_email']) ? 0 : 1;
    // Validate the passed language file.
    if (isset($_POST['lngfile']) && !empty($modSettings['userLanguage'])) {
        // Do we have any languages?
        if (empty($context['languages'])) {
            getLanguages();
        }
        // Did we find it?
        if (isset($context['languages'][$_POST['lngfile']])) {
            $_SESSION['language'] = $_POST['lngfile'];
        } else {
            unset($_POST['lngfile']);
        }
    } else {
        unset($_POST['lngfile']);
    }
    // Set the options needed for registration.
    $regOptions = array('interface' => 'guest', 'username' => !empty($_POST['user']) ? $_POST['user'] : '', 'email' => !empty($_POST['email']) ? $_POST['email'] : '', 'password' => !empty($_POST['passwrd1']) ? $_POST['passwrd1'] : '', 'password_check' => !empty($_POST['passwrd2']) ? $_POST['passwrd2'] : '', 'openid' => !empty($_POST['openid_identifier']) ? $_POST['openid_identifier'] : '', 'auth_method' => !empty($_POST['authenticate']) ? $_POST['authenticate'] : '', 'check_reserved_name' => true, 'check_password_strength' => true, 'check_email_ban' => true, 'send_welcome_email' => !empty($modSettings['send_welcomeEmail']), 'require' => !empty($modSettings['coppaAge']) && !$verifiedOpenID && empty($_SESSION['skip_coppa']) ? 'coppa' : (empty($modSettings['registration_method']) ? 'nothing' : ($modSettings['registration_method'] == 1 ? 'activation' : 'approval')), 'extra_register_vars' => array(), 'theme_vars' => array());
    // Include the additional options that might have been filled in.
    foreach ($possible_strings as $var) {
        if (isset($_POST[$var])) {
            $regOptions['extra_register_vars'][$var] = commonAPI::htmlspecialchars($_POST[$var], ENT_QUOTES);
        }
    }
    foreach ($possible_ints as $var) {
        if (isset($_POST[$var])) {
            $regOptions['extra_register_vars'][$var] = (int) $_POST[$var];
        }
    }
    foreach ($possible_floats as $var) {
        if (isset($_POST[$var])) {
            $regOptions['extra_register_vars'][$var] = (double) $_POST[$var];
        }
    }
    foreach ($possible_bools as $var) {
        if (isset($_POST[$var])) {
            $regOptions['extra_register_vars'][$var] = empty($_POST[$var]) ? 0 : 1;
        }
    }
    // Registration options are always default options...
    if (isset($_POST['default_options'])) {
        $_POST['options'] = isset($_POST['options']) ? $_POST['options'] + $_POST['default_options'] : $_POST['default_options'];
    }
    $regOptions['theme_vars'] = isset($_POST['options']) && is_array($_POST['options']) ? $_POST['options'] : array();
    // Make sure they are clean, dammit!
    $regOptions['theme_vars'] = htmlspecialchars__recursive($regOptions['theme_vars']);
    // If Quick Reply hasn't been set then set it to be shown but collapsed.
    if (!isset($regOptions['theme_vars']['display_quick_reply'])) {
        $regOptions['theme_vars']['display_quick_reply'] = 1;
    }
    // Check whether we have fields that simply MUST be displayed?
    $request = smf_db_query('
		SELECT col_name, field_name, field_type, field_length, mask, show_reg
		FROM {db_prefix}custom_fields
		WHERE active = {int:is_active}', array('is_active' => 1));
    $custom_field_errors = array();
    while ($row = mysql_fetch_assoc($request)) {
        // Don't allow overriding of the theme variables.
        if (isset($regOptions['theme_vars'][$row['col_name']])) {
            unset($regOptions['theme_vars'][$row['col_name']]);
        }
        // Not actually showing it then?
        if (!$row['show_reg']) {
            continue;
        }
        // Prepare the value!
        $value = isset($_POST['customfield'][$row['col_name']]) ? trim($_POST['customfield'][$row['col_name']]) : '';
        // We only care for text fields as the others are valid to be empty.
        if (!in_array($row['field_type'], array('check', 'select', 'radio'))) {
            // Is it too long?
            if ($row['field_length'] && $row['field_length'] < commonAPI::strlen($value)) {
                $custom_field_errors[] = array('custom_field_too_long', array($row['field_name'], $row['field_length']));
            }
            // Any masks to apply?
            if ($row['field_type'] == 'text' && !empty($row['mask']) && $row['mask'] != 'none') {
                //!!! We never error on this - just ignore it at the moment...
                if ($row['mask'] == 'email' && (preg_match('~^[0-9A-Za-z=_+\\-/][0-9A-Za-z=_\'+\\-/\\.]*@[\\w\\-]+(\\.[\\w\\-]+)*(\\.[\\w]{2,6})$~', $value) === 0 || strlen($value) > 255)) {
                    $custom_field_errors[] = array('custom_field_invalid_email', array($row['field_name']));
                } elseif ($row['mask'] == 'number' && preg_match('~[^\\d]~', $value)) {
                    $custom_field_errors[] = array('custom_field_not_number', array($row['field_name']));
                } elseif (substr($row['mask'], 0, 5) == 'regex' && preg_match(substr($row['mask'], 5), $value) === 0) {
                    $custom_field_errors[] = array('custom_field_inproper_format', array($row['field_name']));
                }
            }
        }
        // Is this required but not there?
        if (trim($value) == '' && $row['show_reg'] > 1) {
            $custom_field_errors[] = array('custom_field_empty', array($row['field_name']));
        }
    }
    mysql_free_result($request);
    // Process any errors.
    if (!empty($custom_field_errors)) {
        loadLanguage('Errors');
        foreach ($custom_field_errors as $error) {
            $reg_errors[] = vsprintf($txt['error_' . $error[0]], $error[1]);
        }
    }
    // Lets check for other errors before trying to register the member.
    if (!empty($reg_errors)) {
        $_REQUEST['step'] = 2;
        return Register($reg_errors);
    }
    // If they're wanting to use OpenID we need to validate them first.
    if (empty($_SESSION['openid']['verified']) && !empty($_POST['authenticate']) && $_POST['authenticate'] == 'openid') {
        // What do we need to save?
        $save_variables = array();
        foreach ($_POST as $k => $v) {
            if (!in_array($k, array('sc', 'sesc', $context['session_var'], 'passwrd1', 'passwrd2', 'regSubmit'))) {
                $save_variables[$k] = $v;
            }
        }
        require_once $sourcedir . '/lib/Subs-OpenID.php';
        smf_openID_validate($_POST['openid_identifier'], false, $save_variables);
    } elseif ($verifiedOpenID || !empty($_POST['openid_identifier']) && $_POST['authenticate'] == 'openid') {
        $regOptions['username'] = !empty($_POST['user']) && trim($_POST['user']) != '' ? $_POST['user'] : $_SESSION['openid']['nickname'];
        $regOptions['email'] = !empty($_POST['email']) && trim($_POST['email']) != '' ? $_POST['email'] : $_SESSION['openid']['email'];
        $regOptions['auth_method'] = 'openid';
        $regOptions['openid'] = !empty($_POST['openid_identifier']) ? $_POST['openid_identifier'] : $_SESSION['openid']['openid_uri'];
    }
    $memberID = registerMember($regOptions, true);
    // What there actually an error of some kind dear boy?
    if (is_array($memberID)) {
        $reg_errors = array_merge($reg_errors, $memberID);
        $_REQUEST['step'] = 2;
        return Register($reg_errors);
    }
    // Do our spam protection now.
    spamProtection('register');
    HookAPI::callHook('register_process');
    // We'll do custom fields after as then we get to use the helper function!
    if (!empty($_POST['customfield'])) {
        require_once $sourcedir . '/Profile.php';
        require_once $sourcedir . '/Profile-Modify.php';
        makeCustomFieldChanges($memberID, 'register');
    }
    // If COPPA has been selected then things get complicated, setup the template.
    if (!empty($modSettings['coppaAge']) && empty($_SESSION['skip_coppa'])) {
        redirectexit('action=coppa;member=' . $memberID);
    } elseif (!empty($modSettings['registration_method'])) {
        EoS_Smarty::loadTemplate('register/base');
        EoS_Smarty::getConfigInstance()->registerHookTemplate('register_content_area', 'register/done');
        $context += array('page_title' => $txt['register'], 'title' => $txt['registration_successful'], 'description' => $modSettings['registration_method'] == 2 ? $txt['approval_after_registration'] : $txt['activate_after_registration']);
    } else {
        HookAPI::callHook('integrate_activate', array($row['member_name']));
        setLoginCookie(60 * $modSettings['cookieTime'], $memberID, sha1(sha1(strtolower($regOptions['username']) . $regOptions['password']) . $regOptions['register_vars']['password_salt']));
        redirectexit('action=login2;sa=check;member=' . $memberID, $context['server']['needs_login_fix']);
    }
}
 /**
  * Shows the contact form for the user to fill out
  * Needs to be enabled to be used
  */
 public function action_contact()
 {
     global $context, $txt, $user_info, $modSettings;
     // Already inside, no need to use this, just send a PM
     // Disabled, you cannot enter.
     if (!$user_info['is_guest'] || empty($modSettings['enable_contactform']) || $modSettings['enable_contactform'] == 'disabled') {
         redirectexit();
     }
     loadLanguage('Login');
     loadTemplate('Register');
     if (isset($_REQUEST['send'])) {
         checkSession('post');
         validateToken('contact');
         spamProtection('contact');
         // No errors, yet.
         $context['errors'] = array();
         loadLanguage('Errors');
         // Could they get the right send topic verification code?
         require_once SUBSDIR . '/VerificationControls.class.php';
         require_once SUBSDIR . '/Members.subs.php';
         // form validation
         require_once SUBSDIR . '/DataValidator.class.php';
         $validator = new Data_Validator();
         $validator->sanitation_rules(array('emailaddress' => 'trim', 'contactmessage' => 'trim|Util::htmlspecialchars'));
         $validator->validation_rules(array('emailaddress' => 'required|valid_email', 'contactmessage' => 'required'));
         $validator->text_replacements(array('emailaddress' => $txt['error_email'], 'contactmessage' => $txt['error_message']));
         // Any form errors
         if (!$validator->validate($_POST)) {
             $context['errors'] = $validator->validation_errors();
         }
         // How about any verification errors
         $verificationOptions = array('id' => 'contactform');
         $context['require_verification'] = create_control_verification($verificationOptions, true);
         if (is_array($context['require_verification'])) {
             foreach ($context['require_verification'] as $error) {
                 $context['errors'][] = $txt['error_' . $error];
             }
         }
         // No errors, then send the PM to the admins
         if (empty($context['errors'])) {
             $admins = admins();
             if (!empty($admins)) {
                 require_once SUBSDIR . '/PersonalMessage.subs.php';
                 sendpm(array('to' => array_keys($admins), 'bcc' => array()), $txt['contact_subject'], $_REQUEST['contactmessage'], false, array('id' => 0, 'name' => $validator->emailaddress, 'username' => $validator->emailaddress));
             }
             // Send the PM
             redirectexit('action=contact;sa=done');
         } else {
             $context['emailaddress'] = $validator->emailaddress;
             $context['contactmessage'] = $validator->contactmessage;
         }
     }
     if (isset($_GET['sa']) && $_GET['sa'] == 'done') {
         $context['sub_template'] = 'contact_form_done';
     } else {
         $context['sub_template'] = 'contact_form';
         $context['page_title'] = $txt['admin_contact_form'];
         require_once SUBSDIR . '/VerificationControls.class.php';
         $verificationOptions = array('id' => 'contactform');
         $context['require_verification'] = create_control_verification($verificationOptions);
         $context['visual_verification_id'] = $verificationOptions['id'];
     }
     createToken('contact');
 }
Beispiel #4
0
function Post2()
{
    global $board, $topic, $txt, $modSettings, $sourcedir, $context;
    global $user_info, $board_info, $options, $smcFunc;
    // Sneaking off, are we?
    if (empty($_POST) && empty($topic)) {
        redirectexit('action=post;board=' . $board . '.0');
    } elseif (empty($_POST) && !empty($topic)) {
        redirectexit('action=post;topic=' . $topic . '.0');
    }
    // No need!
    $context['robot_no_index'] = true;
    // If we came from WYSIWYG then turn it back into BBC regardless.
    if (!empty($_REQUEST['message_mode']) && isset($_REQUEST['message'])) {
        require_once $sourcedir . '/Subs-Editor.php';
        $_REQUEST['message'] = html_to_bbc($_REQUEST['message']);
        // We need to unhtml it now as it gets done shortly.
        $_REQUEST['message'] = un_htmlspecialchars($_REQUEST['message']);
        // We need this for everything else.
        $_POST['message'] = $_REQUEST['message'];
    }
    // Previewing? Go back to start.
    if (isset($_REQUEST['preview'])) {
        return Post();
    }
    // Prevent double submission of this form.
    checkSubmitOnce('check');
    // No errors as yet.
    $post_errors = array();
    // If the session has timed out, let the user re-submit their form.
    if (checkSession('post', '', false) != '') {
        $post_errors[] = 'session_timeout';
    }
    // Wrong verification code?
    if (!$user_info['is_admin'] && !$user_info['is_mod'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || $user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1)) {
        require_once $sourcedir . '/Subs-Editor.php';
        $verificationOptions = array('id' => 'post');
        $context['require_verification'] = create_control_verification($verificationOptions, true);
        if (is_array($context['require_verification'])) {
            $post_errors = array_merge($post_errors, $context['require_verification']);
        }
    }
    require_once $sourcedir . '/Subs-Post.php';
    loadLanguage('Post');
    // If this isn't a new topic load the topic info that we need.
    if (!empty($topic)) {
        $request = $smcFunc['db_query']('', '
			SELECT locked, is_sticky, id_poll, approved, id_first_msg, id_last_msg, id_member_started, id_board
			FROM {db_prefix}topics
			WHERE id_topic = {int:current_topic}
			LIMIT 1', array('current_topic' => $topic));
        $topic_info = $smcFunc['db_fetch_assoc']($request);
        $smcFunc['db_free_result']($request);
        // Though the topic should be there, it might have vanished.
        if (!is_array($topic_info)) {
            fatal_lang_error('topic_doesnt_exist');
        }
        // Did this topic suddenly move? Just checking...
        if ($topic_info['id_board'] != $board) {
            fatal_lang_error('not_a_topic');
        }
    }
    // Replying to a topic?
    if (!empty($topic) && !isset($_REQUEST['msg'])) {
        // Don't allow a post if it's locked.
        if ($topic_info['locked'] != 0 && !allowedTo('moderate_board')) {
            fatal_lang_error('topic_locked', false);
        }
        // Sorry, multiple polls aren't allowed... yet.  You should stop giving me ideas :P.
        if (isset($_REQUEST['poll']) && $topic_info['id_poll'] > 0) {
            unset($_REQUEST['poll']);
        }
        // Do the permissions and approval stuff...
        $becomesApproved = true;
        if ($topic_info['id_member_started'] != $user_info['id']) {
            if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any')) {
                $becomesApproved = false;
            } else {
                isAllowedTo('post_reply_any');
            }
        } elseif (!allowedTo('post_reply_any')) {
            if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own')) {
                $becomesApproved = false;
            } else {
                isAllowedTo('post_reply_own');
            }
        }
        if (isset($_POST['lock'])) {
            // Nothing is changed to the lock.
            if (empty($topic_info['locked']) && empty($_POST['lock']) || !empty($_POST['lock']) && !empty($topic_info['locked'])) {
                unset($_POST['lock']);
            } elseif (!allowedTo(array('lock_any', 'lock_own')) || !allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']) {
                unset($_POST['lock']);
            } elseif (!allowedTo('lock_any')) {
                // You cannot override a moderator lock.
                if ($topic_info['locked'] == 1) {
                    unset($_POST['lock']);
                } else {
                    $_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
                }
            } else {
                $_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
            }
        }
        // So you wanna (un)sticky this...let's see.
        if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || $_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky'))) {
            unset($_POST['sticky']);
        }
        // If the number of replies has changed, if the setting is enabled, go back to Post() - which handles the error.
        if (empty($options['no_new_reply_warning']) && isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg']) {
            $_REQUEST['preview'] = true;
            return Post();
        }
        $posterIsGuest = $user_info['is_guest'];
    } elseif (empty($topic)) {
        // Now don't be silly, new topics will get their own id_msg soon enough.
        unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
        // Do like, the permissions, for safety and stuff...
        $becomesApproved = true;
        if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics')) {
            $becomesApproved = false;
        } else {
            isAllowedTo('post_new');
        }
        if (isset($_POST['lock'])) {
            // New topics are by default not locked.
            if (empty($_POST['lock'])) {
                unset($_POST['lock']);
            } elseif (!allowedTo(array('lock_any', 'lock_own'))) {
                unset($_POST['lock']);
            } else {
                $_POST['lock'] = allowedTo('lock_any') ? 1 : 2;
            }
        }
        if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || empty($_POST['sticky']) || !allowedTo('make_sticky'))) {
            unset($_POST['sticky']);
        }
        $posterIsGuest = $user_info['is_guest'];
    } elseif (isset($_REQUEST['msg']) && !empty($topic)) {
        $_REQUEST['msg'] = (int) $_REQUEST['msg'];
        $request = $smcFunc['db_query']('', '
			SELECT id_member, poster_name, poster_email, poster_time, approved
			FROM {db_prefix}messages
			WHERE id_msg = {int:id_msg}
			LIMIT 1', array('id_msg' => $_REQUEST['msg']));
        if ($smcFunc['db_num_rows']($request) == 0) {
            fatal_lang_error('cant_find_messages', false);
        }
        $row = $smcFunc['db_fetch_assoc']($request);
        $smcFunc['db_free_result']($request);
        if (!empty($topic_info['locked']) && !allowedTo('moderate_board')) {
            fatal_lang_error('topic_locked', false);
        }
        if (isset($_POST['lock'])) {
            // Nothing changes to the lock status.
            if (empty($_POST['lock']) && empty($topic_info['locked']) || !empty($_POST['lock']) && !empty($topic_info['locked'])) {
                unset($_POST['lock']);
            } elseif (!allowedTo(array('lock_any', 'lock_own')) || !allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']) {
                unset($_POST['lock']);
            } elseif (!allowedTo('lock_any')) {
                // You're not allowed to break a moderator's lock.
                if ($topic_info['locked'] == 1) {
                    unset($_POST['lock']);
                } else {
                    $_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
                }
            } else {
                $_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
            }
        }
        // Change the sticky status of this topic?
        if (isset($_POST['sticky']) && (!allowedTo('make_sticky') || $_POST['sticky'] == $topic_info['is_sticky'])) {
            unset($_POST['sticky']);
        }
        if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any')) {
            if ((!$modSettings['postmod_active'] || $row['approved']) && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time()) {
                fatal_lang_error('modify_post_time_passed', false);
            } elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_own')) {
                isAllowedTo('modify_replies');
            } else {
                isAllowedTo('modify_own');
            }
        } elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_any')) {
            isAllowedTo('modify_replies');
            // If you're modifying a reply, I say it better be logged...
            $moderationAction = true;
        } else {
            isAllowedTo('modify_any');
            // Log it, assuming you're not modifying your own post.
            if ($row['id_member'] != $user_info['id']) {
                $moderationAction = true;
            }
        }
        $posterIsGuest = empty($row['id_member']);
        // Can they approve it?
        $can_approve = allowedTo('approve_posts');
        $becomesApproved = $modSettings['postmod_active'] ? $can_approve && !$row['approved'] ? !empty($_REQUEST['approve']) ? 1 : 0 : $row['approved'] : 1;
        $approve_has_changed = $row['approved'] != $becomesApproved;
        if (!allowedTo('moderate_forum') || !$posterIsGuest) {
            $_POST['guestname'] = $row['poster_name'];
            $_POST['email'] = $row['poster_email'];
        }
    }
    // If the poster is a guest evaluate the legality of name and email.
    if ($posterIsGuest) {
        $_POST['guestname'] = !isset($_POST['guestname']) ? '' : trim($_POST['guestname']);
        $_POST['email'] = !isset($_POST['email']) ? '' : trim($_POST['email']);
        if ($_POST['guestname'] == '' || $_POST['guestname'] == '_') {
            $post_errors[] = 'no_name';
        }
        if ($smcFunc['strlen']($_POST['guestname']) > 25) {
            $post_errors[] = 'long_name';
        }
        if (empty($modSettings['guest_post_no_email'])) {
            // Only check if they changed it!
            if (!isset($row) || $row['poster_email'] != $_POST['email']) {
                if (!allowedTo('moderate_forum') && (!isset($_POST['email']) || $_POST['email'] == '')) {
                    $post_errors[] = 'no_email';
                }
                if (!allowedTo('moderate_forum') && preg_match('~^[0-9A-Za-z=_+\\-/][0-9A-Za-z=_\'+\\-/\\.]*@[\\w\\-]+(\\.[\\w\\-]+)*(\\.[\\w]{2,6})$~', $_POST['email']) == 0) {
                    $post_errors[] = 'bad_email';
                }
            }
            // Now make sure this email address is not banned from posting.
            isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
        }
        // In case they are making multiple posts this visit, help them along by storing their name.
        if (empty($post_errors)) {
            $_SESSION['guest_name'] = $_POST['guestname'];
            $_SESSION['guest_email'] = $_POST['email'];
        }
    }
    // Check the subject and message.
    if (!isset($_POST['subject']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['subject'])) === '') {
        $post_errors[] = 'no_subject';
    }
    if (!isset($_POST['message']) || $smcFunc['htmltrim']($smcFunc['htmlspecialchars']($_POST['message']), ENT_QUOTES) === '') {
        $post_errors[] = 'no_message';
    } elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_POST['message']) > $modSettings['max_messageLength']) {
        $post_errors[] = 'long_message';
    } else {
        // Prepare the message a bit for some additional testing.
        $_POST['message'] = $smcFunc['htmlspecialchars']($_POST['message'], ENT_QUOTES);
        // Preparse code. (Zef)
        if ($user_info['is_guest']) {
            $user_info['name'] = $_POST['guestname'];
        }
        preparsecode($_POST['message']);
        // Let's see if there's still some content left without the tags.
        if ($smcFunc['htmltrim'](strip_tags(parse_bbc($_POST['message'], false), '<img>')) === '' && (!allowedTo('admin_forum') || strpos($_POST['message'], '[html]') === false)) {
            $post_errors[] = 'no_message';
        }
    }
    if (isset($_POST['calendar']) && !isset($_REQUEST['deleteevent']) && $smcFunc['htmltrim']($_POST['evtitle']) === '') {
        $post_errors[] = 'no_event';
    }
    // You are not!
    if (isset($_POST['message']) && strtolower($_POST['message']) == 'i am the administrator.' && !$user_info['is_admin']) {
        fatal_error('Knave! Masquerader! Charlatan!', false);
    }
    // Validate the poll...
    if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1') {
        if (!empty($topic) && !isset($_REQUEST['msg'])) {
            fatal_lang_error('no_access', false);
        }
        // This is a new topic... so it's a new poll.
        if (empty($topic)) {
            isAllowedTo('poll_post');
        } elseif ($user_info['id'] == $topic_info['id_member_started'] && !allowedTo('poll_add_any')) {
            isAllowedTo('poll_add_own');
        } else {
            isAllowedTo('poll_add_any');
        }
        if (!isset($_POST['question']) || trim($_POST['question']) == '') {
            $post_errors[] = 'no_question';
        }
        $_POST['options'] = empty($_POST['options']) ? array() : htmltrim__recursive($_POST['options']);
        // Get rid of empty ones.
        foreach ($_POST['options'] as $k => $option) {
            if ($option == '') {
                unset($_POST['options'][$k], $_POST['options'][$k]);
            }
        }
        // What are you going to vote between with one choice?!?
        if (count($_POST['options']) < 2) {
            $post_errors[] = 'poll_few';
        }
    }
    if ($posterIsGuest) {
        // If user is a guest, make sure the chosen name isn't taken.
        require_once $sourcedir . '/Subs-Members.php';
        if (isReservedName($_POST['guestname'], 0, true, false) && (!isset($row['poster_name']) || $_POST['guestname'] != $row['poster_name'])) {
            $post_errors[] = 'bad_name';
        }
    } elseif (!isset($_REQUEST['msg'])) {
        $_POST['guestname'] = $user_info['username'];
        $_POST['email'] = $user_info['email'];
    }
    // Any mistakes?
    if (!empty($post_errors)) {
        loadLanguage('Errors');
        // Previewing.
        $_REQUEST['preview'] = true;
        $context['post_error'] = array('messages' => array());
        foreach ($post_errors as $post_error) {
            $context['post_error'][$post_error] = true;
            if ($post_error == 'long_message') {
                $txt['error_' . $post_error] = sprintf($txt['error_' . $post_error], $modSettings['max_messageLength']);
            }
            $context['post_error']['messages'][] = $txt['error_' . $post_error];
        }
        return Post();
    }
    // Make sure the user isn't spamming the board.
    if (!isset($_REQUEST['msg'])) {
        spamProtection('post');
    }
    // At about this point, we're posting and that's that.
    ignore_user_abort(true);
    @set_time_limit(300);
    // Add special html entities to the subject, name, and email.
    $_POST['subject'] = strtr($smcFunc['htmlspecialchars']($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
    $_POST['guestname'] = htmlspecialchars($_POST['guestname']);
    $_POST['email'] = htmlspecialchars($_POST['email']);
    // At this point, we want to make sure the subject isn't too long.
    if ($smcFunc['strlen']($_POST['subject']) > 100) {
        $_POST['subject'] = $smcFunc['substr']($_POST['subject'], 0, 100);
    }
    // Make the poll...
    if (isset($_REQUEST['poll'])) {
        // Make sure that the user has not entered a ridiculous number of options..
        if (empty($_POST['poll_max_votes']) || $_POST['poll_max_votes'] <= 0) {
            $_POST['poll_max_votes'] = 1;
        } elseif ($_POST['poll_max_votes'] > count($_POST['options'])) {
            $_POST['poll_max_votes'] = count($_POST['options']);
        } else {
            $_POST['poll_max_votes'] = (int) $_POST['poll_max_votes'];
        }
        $_POST['poll_expire'] = (int) $_POST['poll_expire'];
        $_POST['poll_expire'] = $_POST['poll_expire'] > 9999 ? 9999 : ($_POST['poll_expire'] < 0 ? 0 : $_POST['poll_expire']);
        // Just set it to zero if it's not there..
        if (!isset($_POST['poll_hide'])) {
            $_POST['poll_hide'] = 0;
        } else {
            $_POST['poll_hide'] = (int) $_POST['poll_hide'];
        }
        $_POST['poll_change_vote'] = isset($_POST['poll_change_vote']) ? 1 : 0;
        $_POST['poll_guest_vote'] = isset($_POST['poll_guest_vote']) ? 1 : 0;
        // Make sure guests are actually allowed to vote generally.
        if ($_POST['poll_guest_vote']) {
            require_once $sourcedir . '/Subs-Members.php';
            $allowedVoteGroups = groupsAllowedTo('poll_vote', $board);
            if (!in_array(-1, $allowedVoteGroups['allowed'])) {
                $_POST['poll_guest_vote'] = 0;
            }
        }
        // If the user tries to set the poll too far in advance, don't let them.
        if (!empty($_POST['poll_expire']) && $_POST['poll_expire'] < 1) {
            fatal_lang_error('poll_range_error', false);
        } elseif (empty($_POST['poll_expire']) && $_POST['poll_hide'] == 2) {
            $_POST['poll_hide'] = 1;
        }
        // Clean up the question and answers.
        $_POST['question'] = htmlspecialchars($_POST['question']);
        $_POST['question'] = $smcFunc['truncate']($_POST['question'], 255);
        $_POST['question'] = preg_replace('~&amp;#(\\d{4,5}|[2-9]\\d{2,4}|1[2-9]\\d);~', '&#$1;', $_POST['question']);
        $_POST['options'] = htmlspecialchars__recursive($_POST['options']);
    }
    // Check if they are trying to delete any current attachments....
    if (isset($_REQUEST['msg'], $_POST['attach_del']) && (allowedTo('post_attachment') || $modSettings['postmod_active'] && allowedTo('post_unapproved_attachments'))) {
        $del_temp = array();
        foreach ($_POST['attach_del'] as $i => $dummy) {
            $del_temp[$i] = (int) $dummy;
        }
        require_once $sourcedir . '/ManageAttachments.php';
        $attachmentQuery = array('attachment_type' => 0, 'id_msg' => (int) $_REQUEST['msg'], 'not_id_attach' => $del_temp);
        removeAttachments($attachmentQuery);
    }
    // ...or attach a new file...
    if (isset($_FILES['attachment']['name']) || !empty($_SESSION['temp_attachments']) && empty($_POST['from_qr'])) {
        // Verify they can post them!
        if (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_attachments')) {
            isAllowedTo('post_attachment');
        }
        // Make sure we're uploading to the right place.
        if (!empty($modSettings['currentAttachmentUploadDir'])) {
            if (!is_array($modSettings['attachmentUploadDir'])) {
                $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
            }
            // The current directory, of course!
            $current_attach_dir = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
        } else {
            $current_attach_dir = $modSettings['attachmentUploadDir'];
        }
        // If this isn't a new post, check the current attachments.
        if (isset($_REQUEST['msg'])) {
            $request = $smcFunc['db_query']('', '
				SELECT COUNT(*), SUM(size)
				FROM {db_prefix}attachments
				WHERE id_msg = {int:id_msg}
					AND attachment_type = {int:attachment_type}', array('id_msg' => (int) $_REQUEST['msg'], 'attachment_type' => 0));
            list($quantity, $total_size) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
        } else {
            $quantity = 0;
            $total_size = 0;
        }
        if (!empty($_SESSION['temp_attachments'])) {
            foreach ($_SESSION['temp_attachments'] as $attachID => $name) {
                if (preg_match('~^post_tmp_' . $user_info['id'] . '_\\d+$~', $attachID) == 0) {
                    continue;
                }
                if (!empty($_POST['attach_del']) && !in_array($attachID, $_POST['attach_del'])) {
                    unset($_SESSION['temp_attachments'][$attachID]);
                    @unlink($current_attach_dir . '/' . $attachID);
                    continue;
                }
                $_FILES['attachment']['tmp_name'][] = $attachID;
                $_FILES['attachment']['name'][] = $name;
                $_FILES['attachment']['size'][] = filesize($current_attach_dir . '/' . $attachID);
                list($_FILES['attachment']['width'][], $_FILES['attachment']['height'][]) = @getimagesize($current_attach_dir . '/' . $attachID);
                unset($_SESSION['temp_attachments'][$attachID]);
            }
        }
        if (!isset($_FILES['attachment']['name'])) {
            $_FILES['attachment']['tmp_name'] = array();
        }
        $attachIDs = array();
        foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy) {
            if ($_FILES['attachment']['name'][$n] == '') {
                continue;
            }
            // Have we reached the maximum number of files we are allowed?
            $quantity++;
            if (!empty($modSettings['attachmentNumPerPostLimit']) && $quantity > $modSettings['attachmentNumPerPostLimit']) {
                checkSubmitOnce('free');
                fatal_lang_error('attachments_limit_per_post', false, array($modSettings['attachmentNumPerPostLimit']));
            }
            // Check the total upload size for this post...
            $total_size += $_FILES['attachment']['size'][$n];
            if (!empty($modSettings['attachmentPostLimit']) && $total_size > $modSettings['attachmentPostLimit'] * 1024) {
                checkSubmitOnce('free');
                fatal_lang_error('file_too_big', false, array($modSettings['attachmentPostLimit']));
            }
            $attachmentOptions = array('post' => isset($_REQUEST['msg']) ? $_REQUEST['msg'] : 0, 'poster' => $user_info['id'], 'name' => $_FILES['attachment']['name'][$n], 'tmp_name' => $_FILES['attachment']['tmp_name'][$n], 'size' => $_FILES['attachment']['size'][$n], 'approved' => !$modSettings['postmod_active'] || allowedTo('post_attachment'));
            if (createAttachment($attachmentOptions)) {
                $attachIDs[] = $attachmentOptions['id'];
                if (!empty($attachmentOptions['thumb'])) {
                    $attachIDs[] = $attachmentOptions['thumb'];
                }
            } else {
                if (in_array('could_not_upload', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_lang_error('attach_timeout', 'critical');
                }
                if (in_array('too_large', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_lang_error('file_too_big', false, array($modSettings['attachmentSizeLimit']));
                }
                if (in_array('bad_extension', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_error($attachmentOptions['name'] . '.<br />' . $txt['cant_upload_type'] . ' ' . $modSettings['attachmentExtensions'] . '.', false);
                }
                if (in_array('directory_full', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_lang_error('ran_out_of_space', 'critical');
                }
                if (in_array('bad_filename', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_error(basename($attachmentOptions['name']) . '.<br />' . $txt['restricted_filename'] . '.', 'critical');
                }
                if (in_array('taken_filename', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_lang_error('filename_exists');
                }
                if (in_array('bad_attachment', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_lang_error('bad_attachment');
                }
            }
        }
    }
    // Make the poll...
    if (isset($_REQUEST['poll'])) {
        // Create the poll.
        $smcFunc['db_insert']('', '{db_prefix}polls', array('question' => 'string-255', 'hide_results' => 'int', 'max_votes' => 'int', 'expire_time' => 'int', 'id_member' => 'int', 'poster_name' => 'string-255', 'change_vote' => 'int', 'guest_vote' => 'int'), array($_POST['question'], $_POST['poll_hide'], $_POST['poll_max_votes'], empty($_POST['poll_expire']) ? 0 : time() + $_POST['poll_expire'] * 3600 * 24, $user_info['id'], $_POST['guestname'], $_POST['poll_change_vote'], $_POST['poll_guest_vote']), array('id_poll'));
        $id_poll = $smcFunc['db_insert_id']('{db_prefix}polls', 'id_poll');
        // Create each answer choice.
        $i = 0;
        $pollOptions = array();
        foreach ($_POST['options'] as $option) {
            $pollOptions[] = array($id_poll, $i, $option);
            $i++;
        }
        $smcFunc['db_insert']('insert', '{db_prefix}poll_choices', array('id_poll' => 'int', 'id_choice' => 'int', 'label' => 'string-255'), $pollOptions, array('id_poll', 'id_choice'));
    } else {
        $id_poll = 0;
    }
    // Creating a new topic?
    $newTopic = empty($_REQUEST['msg']) && empty($topic);
    $_POST['icon'] = !empty($attachIDs) && $_POST['icon'] == 'xx' ? 'clip' : $_POST['icon'];
    // Collect all parameters for the creation or modification of a post.
    $msgOptions = array('id' => empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg'], 'subject' => $_POST['subject'], 'body' => $_POST['message'], 'icon' => preg_replace('~[\\./\\\\*:"\'<>]~', '', $_POST['icon']), 'smileys_enabled' => !isset($_POST['ns']), 'attachments' => empty($attachIDs) ? array() : $attachIDs, 'approved' => $becomesApproved);
    $topicOptions = array('id' => empty($topic) ? 0 : $topic, 'board' => $board, 'poll' => isset($_REQUEST['poll']) ? $id_poll : null, 'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null, 'sticky_mode' => isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics']) ? (int) $_POST['sticky'] : null, 'mark_as_read' => true, 'is_approved' => !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']));
    $posterOptions = array('id' => $user_info['id'], 'name' => $_POST['guestname'], 'email' => $_POST['email'], 'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count']);
    // This is an already existing message. Edit it.
    if (!empty($_REQUEST['msg'])) {
        // Have admins allowed people to hide their screwups?
        if (time() - $row['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $row['id_member']) {
            $msgOptions['modify_time'] = time();
            $msgOptions['modify_name'] = $user_info['name'];
        }
        // This will save some time...
        if (empty($approve_has_changed)) {
            unset($msgOptions['approved']);
        }
        modifyPost($msgOptions, $topicOptions, $posterOptions);
    } else {
        createPost($msgOptions, $topicOptions, $posterOptions);
        if (isset($topicOptions['id'])) {
            $topic = $topicOptions['id'];
        }
    }
    // Editing or posting an event?
    if (isset($_POST['calendar']) && (!isset($_REQUEST['eventid']) || $_REQUEST['eventid'] == -1)) {
        require_once $sourcedir . '/Subs-Calendar.php';
        // Make sure they can link an event to this post.
        canLinkEvent();
        // Insert the event.
        $eventOptions = array('board' => $board, 'topic' => $topic, 'title' => $_POST['evtitle'], 'member' => $user_info['id'], 'start_date' => sprintf('%04d-%02d-%02d', $_POST['year'], $_POST['month'], $_POST['day']), 'span' => isset($_POST['span']) && $_POST['span'] > 0 ? min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1) : 0);
        insertEvent($eventOptions);
    } elseif (isset($_POST['calendar'])) {
        $_REQUEST['eventid'] = (int) $_REQUEST['eventid'];
        // Validate the post...
        require_once $sourcedir . '/Subs-Calendar.php';
        validateEventPost();
        // If you're not allowed to edit any events, you have to be the poster.
        if (!allowedTo('calendar_edit_any')) {
            // Get the event's poster.
            $request = $smcFunc['db_query']('', '
				SELECT id_member
				FROM {db_prefix}calendar
				WHERE id_event = {int:id_event}', array('id_event' => $_REQUEST['eventid']));
            $row2 = $smcFunc['db_fetch_assoc']($request);
            $smcFunc['db_free_result']($request);
            // Silly hacker, Trix are for kids. ...probably trademarked somewhere, this is FAIR USE! (parody...)
            isAllowedTo('calendar_edit_' . ($row2['id_member'] == $user_info['id'] ? 'own' : 'any'));
        }
        // Delete it?
        if (isset($_REQUEST['deleteevent'])) {
            $smcFunc['db_query']('', '
				DELETE FROM {db_prefix}calendar
				WHERE id_event = {int:id_event}', array('id_event' => $_REQUEST['eventid']));
        } else {
            $span = !empty($modSettings['cal_allowspan']) && !empty($_REQUEST['span']) ? min((int) $modSettings['cal_maxspan'], (int) $_REQUEST['span'] - 1) : 0;
            $start_time = mktime(0, 0, 0, (int) $_REQUEST['month'], (int) $_REQUEST['day'], (int) $_REQUEST['year']);
            $smcFunc['db_query']('', '
				UPDATE {db_prefix}calendar
				SET end_date = {date:end_date},
					start_date = {date:start_date},
					title = {string:title}
				WHERE id_event = {int:id_event}', array('end_date' => strftime('%Y-%m-%d', $start_time + $span * 86400), 'start_date' => strftime('%Y-%m-%d', $start_time), 'id_event' => $_REQUEST['eventid'], 'title' => $smcFunc['htmlspecialchars']($_REQUEST['evtitle'], ENT_QUOTES)));
        }
        updateSettings(array('calendar_updated' => time()));
    }
    // Marking read should be done even for editing messages....
    // Mark all the parents read.  (since you just posted and they will be unread.)
    if (!$user_info['is_guest'] && !empty($board_info['parent_boards'])) {
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}log_boards
			SET id_msg = {int:id_msg}
			WHERE id_member = {int:current_member}
				AND id_board IN ({array_int:board_list})', array('current_member' => $user_info['id'], 'board_list' => array_keys($board_info['parent_boards']), 'id_msg' => $modSettings['maxMsgID']));
    }
    // Turn notification on or off.  (note this just blows smoke if it's already on or off.)
    if (!empty($_POST['notify']) && allowedTo('mark_any_notify')) {
        $smcFunc['db_insert']('ignore', '{db_prefix}log_notify', array('id_member' => 'int', 'id_topic' => 'int', 'id_board' => 'int'), array($user_info['id'], $topic, 0), array('id_member', 'id_topic', 'id_board'));
    } elseif (!$newTopic) {
        $smcFunc['db_query']('', '
			DELETE FROM {db_prefix}log_notify
			WHERE id_member = {int:current_member}
				AND id_topic = {int:current_topic}', array('current_member' => $user_info['id'], 'current_topic' => $topic));
    }
    // Log an act of moderation - modifying.
    if (!empty($moderationAction)) {
        logAction('modify', array('topic' => $topic, 'message' => (int) $_REQUEST['msg'], 'member' => $row['id_member'], 'board' => $board));
    }
    if (isset($_POST['lock']) && $_POST['lock'] != 2) {
        logAction('lock', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
    }
    if (isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics'])) {
        logAction('sticky', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
    }
    // Notify any members who have notification turned on for this topic - only do this if it's going to be approved(!)
    if ($becomesApproved) {
        if ($newTopic) {
            $notifyData = array('body' => $_POST['message'], 'subject' => $_POST['subject'], 'name' => $user_info['name'], 'poster' => $user_info['id'], 'msg' => $msgOptions['id'], 'board' => $board, 'topic' => $topic);
            notifyMembersBoard($notifyData);
        } elseif (empty($_REQUEST['msg'])) {
            // Only send it to everyone if the topic is approved, otherwise just to the topic starter if they want it.
            if ($topic_info['approved']) {
                sendNotifications($topic, 'reply');
            } else {
                sendNotifications($topic, 'reply', array(), $topic_info['id_member_started']);
            }
        }
    }
    // Returning to the topic?
    if (!empty($_REQUEST['goback'])) {
        // Mark the board as read.... because it might get confusing otherwise.
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}log_boards
			SET id_msg = {int:maxMsgID}
			WHERE id_member = {int:current_member}
				AND id_board = {int:current_board}', array('current_board' => $board, 'current_member' => $user_info['id'], 'maxMsgID' => $modSettings['maxMsgID']));
    }
    if ($board_info['num_topics'] == 0) {
        cache_put_data('board-' . $board, null, 120);
    }
    if (!empty($_POST['announce_topic'])) {
        redirectexit('action=announce;sa=selectgroup;topic=' . $topic . (!empty($_POST['move']) && allowedTo('move_any') ? ';move' : '') . (empty($_REQUEST['goback']) ? '' : ';goback'));
    }
    if (!empty($_POST['move']) && allowedTo('move_any')) {
        redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
    }
    // Return to post if the mod is on.
    if (isset($_REQUEST['msg']) && !empty($_REQUEST['goback'])) {
        redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg'], $context['browser']['is_ie']);
    } elseif (!empty($_REQUEST['goback'])) {
        redirectexit('topic=' . $topic . '.new#new', $context['browser']['is_ie']);
    } else {
        redirectexit('board=' . $board . '.0');
    }
}
Beispiel #5
0
function KB_knowcont()
{
    global $smcFunc, $txt, $scripturl, $sourcedir, $boardurl, $modSettings, $user_info, $context;
    $context['sub_template'] = 'kb_knowcont';
    if (isset($_REQUEST['cont'])) {
        if (($listData = cache_get_data('kb_articles_listinfo' . $_GET['cont'] . '', 3600)) === null) {
            $params = array('table' => 'kb_articles AS a', 'call' => 'a.title,a.kbnid,a.id_cat,c.name', 'left_join' => '{db_prefix}kb_category AS c ON (a.id_cat = c.kbid)', 'where' => 'a.kbnid = {int:kbnid}');
            $data = array('kbnid' => (int) $_GET['cont']);
            $listData = KB_ListData($params, $data);
            cache_put_data('kb_articles_listinfo' . $_GET['cont'] . '', $listData, 3600);
        }
        $artname = $listData['title'];
        $aid = $listData['kbnid'];
        $cid = $listData['id_cat'];
        $cname = $listData['name'];
        if (!$aid) {
            fatal_error('' . $txt['kb_pinfi7'] . ' <strong>' . $_GET['cont'] . '</strong> ' . $txt['kb_jumpgo1'] . '', false);
        }
        $context['linktree'][] = array('url' => $scripturl . '?action=kb;area=cats;cat=' . $cid . '', 'name' => $cname);
        $context['linktree'][] = array('url' => $scripturl . '?action=kb;area=article;cont=' . $_GET['cont'] . '', 'name' => $artname);
        if (($context['know'] = cache_get_data('kb_articles' . $_GET['cont'] . '', 3600)) === null) {
            $result = $smcFunc['db_query']('', '
	            SELECT k.kbnid,k.content, k.source, k.title,k.id_cat,k.date,k.id_member,m.real_name, k.views, k.rate, k.approved
	            FROM {db_prefix}kb_articles AS k
		        LEFT JOIN {db_prefix}members AS m ON (k.id_member = m.id_member)
		        LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = m.id_member)
	            WHERE kbnid = {int:kbnid}', array('kbnid' => (int) $_GET['cont']));
            $context['know'] = array();
            while ($row = $smcFunc['db_fetch_assoc']($result)) {
                $context['know'][] = array('content' => KB_parseTags($row['content'], $row['kbnid'], 3), 'title' => parse_bbc($row['title']), 'source' => parse_bbc($row['source']), 'kbnid' => $row['kbnid'], 'approved' => $row['approved'], 'views' => $row['views'], 'rate' => $row['rate'], 'date' => date('D d M Y', $row['date']), 'id_cat' => $row['id_cat'], 'id_member' => $row['id_member'], 'real_name' => $row['real_name']);
            }
            $smcFunc['db_free_result']($result);
            cache_put_data('kb_articles' . $_GET['cont'] . '', $context['know'], 3600);
        }
        $context['page_title'] = $context['know'][0]['title'];
        if ($context['know'][0]['approved'] == 0 && $context['know'][0]['id_member'] != $user_info['id'] && !allowedTo('manage_kb')) {
            fatal_lang_error('kb_articlwnot_approved', false);
        }
        KBisAllowedto($context['know'][0]['id_cat'], 'view');
        $context['kbimg'] = KB_getimages($_GET['cont']);
        if (!empty($modSettings['kb_ecom'])) {
            $context['kbcom'] = KB_getcomments($_GET['cont']);
            KB_showediter(!empty($_POST['description']) ? $_POST['description'] : '', 'description');
        }
        KB_dojprint();
        $query_params = array('table' => 'kb_articles', 'set' => 'views = views + 1', 'where' => 'kbnid = {int:kbnid}');
        $query_data = array('kbnid' => (int) $_GET['cont']);
        kb_UpdateData($query_params, $query_data);
    }
    if ($user_info['is_guest']) {
        require_once $sourcedir . '/Subs-Editor.php';
        $verificationOptions = array('id' => 'register');
        $context['visual_verification'] = create_control_verification($verificationOptions);
        $context['visual_verification_id'] = $verificationOptions['id'];
    }
    //comment
    if (isset($_REQUEST['comment'])) {
        if ($user_info['is_guest']) {
            require_once $sourcedir . '/Subs-Editor.php';
            $verificationOptions = array('id' => 'register');
            $context['visual_verification'] = create_control_verification($verificationOptions, true);
            if (is_array($context['visual_verification'])) {
                loadLanguage('Errors');
                foreach ($context['visual_verification'] as $error) {
                    fatal_error($txt['error_' . $error]);
                }
            }
        }
        isAllowedTo('com_kb');
        checkSession();
        $_POST['description'] = $smcFunc['htmlspecialchars']($_POST['description'], ENT_QUOTES);
        $_GET['arid'] = (int) $_GET['arid'];
        if (empty($_POST['description'])) {
            fatal_lang_error('knowledgebase_emtydesc', false);
        }
        $approved = allowedTo('auto_approvecom_kb') ? 1 : 0;
        $mes = '' . $txt['kb_log_text4'] . ' <strong><a href="' . $scripturl . '?action=kb;area=article;cont=' . $_GET['arid'] . '">' . $context['know'][0]['title'] . '</a></strong>';
        KB_log_actions('add_com', $_GET['arid'], $mes);
        $data = array('table' => 'kb_comments', 'cols' => array('id_article' => 'int', 'comment' => 'string', 'date' => 'int', 'id_member' => 'int', 'approved' => 'int'));
        $values = array($_GET['arid'], $_POST['description'], time(), $user_info['id'], $approved);
        $indexes = array('id_article');
        KB_InsertData($data, $values, $indexes);
        KBrecountcomments();
        KB_cleanCache();
        redirectexit('action=kb;area=article;cont=' . $_GET['arid'] . '');
    }
    if (isset($_REQUEST['commentdel'])) {
        isAllowedTo('comdel_kb');
        $mes = '' . $txt['kb_log_text3'] . ' <strong><a href="' . $scripturl . '?action=kb;area=article;cont=' . $_GET['cont'] . '">' . $context['know'][0]['title'] . '</a></strong>';
        KB_log_actions('del_com', $_GET['cont'], $mes);
        $query_params = array('table' => 'kb_comments', 'where' => 'id = {int:kbid}');
        $query_data = array('kbid' => (int) $_GET['arid']);
        KB_DeleteData($query_params, $query_data);
        KB_cleanCache();
        KBrecountcomments();
        redirectexit('action=kb;area=article;cont=' . $_GET['cont'] . '');
    }
    //approve
    if (isset($_REQUEST['approve'])) {
        checkSession('get');
        $query_params = array('table' => 'kb_articles', 'set' => 'approved = {int:one}', 'where' => 'kbnid = {int:kbnid}');
        $query_data = array('kbnid' => (int) $_REQUEST['aid'], 'one' => 1);
        kb_UpdateData($query_params, $query_data);
        $params = array('table' => 'kb_articles', 'call' => 'id_member, kbnid, title', 'where' => 'kbnid = {int:kbnid}');
        $data = array('kbnid' => (int) $_GET['aid']);
        $listData = KB_ListData($params, $data);
        $nameid = $listData['id_member'];
        $kid = $listData['kbnid'];
        $title = $listData['title'];
        $kbmes = '' . $txt['kb_aapprove1'] . ' [url=' . $scripturl . '?action=kb;area=article;cont=' . $kid . ']' . $txt['kb_aapprove2'] . '[/url] ' . $txt['kb_aapprove3'] . '';
        KB_sendpm($nameid, $txt['kb_aapprove6'], $kbmes);
        $mes = '' . $txt['kb_log_text2'] . ' <strong><a href="' . $scripturl . '?action=kb;area=article;cont=' . $kid . '">' . $title . '</a></strong>';
        KB_log_actions('app_article', $kid, $mes);
        KBrecountItems();
        KB_cleanCache();
        redirectexit('action=kb;area=article;cont=' . $_REQUEST['aid'] . '');
    }
    //unapprove
    if (isset($_REQUEST['unapprove']) && isset($_REQUEST['inap'])) {
        checkSession('get');
        $query_params = array('table' => 'kb_articles', 'set' => 'approved = {int:one}', 'where' => 'kbnid = {int:kbnid}');
        $query_data = array('kbnid' => (int) $_REQUEST['inap'], 'one' => 0);
        kb_UpdateData($query_params, $query_data);
        $params = array('table' => 'kb_articles', 'call' => 'id_member, kbnid, title', 'where' => 'kbnid = {int:kbnid}');
        $data = array('kbnid' => (int) $_GET['inap']);
        $listData = KB_ListData($params, $data);
        $nameid = $listData['id_member'];
        $kid = $listData['kbnid'];
        $title = $listData['title'];
        $kbmes = '' . $txt['kb_aapprove4'] . ' [url=' . $scripturl . '?action=kb;area=article;cont=' . $kid . ']' . $txt['kb_aapprove2'] . '[/url] ' . $txt['kb_aapprove3'] . '';
        KB_sendpm($nameid, $txt['kb_aapprove7'], $kbmes);
        $mes = '' . $txt['kb_log_text1'] . ' <strong><a href="' . $scripturl . '?action=kb;area=article;cont=' . $kid . '">' . $title . '</a></strong>';
        KB_log_actions('unapp_article', $kid, $mes);
        KBrecountItems();
        KB_cleanCache();
        redirectexit('action=kb;area=article;cont=' . $_REQUEST['inap'] . '');
    }
}
Beispiel #6
0
function Contact()
{
    global $context, $mbname, $webmaster_email, $txt, $sourcedir, $user_info, $modSettings, $scripturl, $smcFunc;
    // Check if the current user can send a message
    isAllowedTo('view_contact');
    if (isset($_REQUEST['sa'])) {
        if ($_REQUEST['sa'] == 'save') {
            if ($context['user']['is_guest'] == true) {
                if (isset($modSettings['recaptcha_enabled']) && !empty($modSettings['recaptcha_enabled']) && ($modSettings['recaptcha_enabled'] == 1 && !empty($modSettings['recaptcha_public_key']) && !empty($modSettings['recaptcha_private_key']))) {
                    if (!empty($_POST["recaptcha_response_field"]) && !empty($_POST["recaptcha_challenge_field"])) {
                        require_once "{$sourcedir}/recaptchalib.php";
                        $resp = recaptcha_check_answer($modSettings['recaptcha_private_key'], $_SERVER['REMOTE_ADDR'], $_REQUEST['recaptcha_challenge_field'], $_REQUEST['recaptcha_response_field']);
                        if (!$resp->is_valid) {
                            fatal_lang_error('error_wrong_verification_code', false);
                        }
                    } else {
                        fatal_lang_error('error_wrong_verification_code', false);
                    }
                } else {
                    if (!empty($modSettings['reg_verification'])) {
                        require_once $sourcedir . '/Subs-Editor.php';
                        $verificationOptions = array('id' => 'post');
                        $context['visual_verification'] = create_control_verification($verificationOptions, true);
                        if (is_array($context['visual_verification'])) {
                            loadLanguage('Errors');
                            foreach ($context['visual_verification'] as $error) {
                                fatal_error($txt['error_' . $error], false);
                            }
                        }
                    }
                }
            }
            $from = $_POST['from'];
            if ($from == '') {
                fatal_error($txt['smfcontact_errname'], false);
            }
            $subject = $_POST['subject'];
            if ($subject == '') {
                fatal_error($txt['smfcontact_errsubject'], false);
            }
            $message = $_POST['message'];
            if ($message == '') {
                fatal_error($txt['smfcontact_errmessage'], false);
            }
            $email = $_POST['email'];
            if ($email == '') {
                fatal_error($txt['smfcontact_erremail'], false);
            }
            $subject = $smcFunc['htmlspecialchars']($subject, ENT_QUOTES);
            $message = $smcFunc['htmlspecialchars']($message, ENT_QUOTES);
            $from = $smcFunc['htmlspecialchars']($from, ENT_QUOTES);
            $email = $smcFunc['htmlspecialchars']($email, ENT_QUOTES);
            $m = $txt['smfcontact_form'] . $mbname . " \n";
            $m .= $txt['smfcontact_formname'] . $from . "\n";
            $m .= $txt['smfcontact_formemail'] . $email . "\n";
            $m .= $txt['smfcontact_ip'] . $_SERVER['REMOTE_ADDR'] . "\n";
            $m .= $txt['smfcontact_formmessage'];
            $m .= $message;
            $m .= "\n";
            // For send mail function
            require_once $sourcedir . '/Subs-Post.php';
            // Send email to webmaster
            if (empty($modSettings['smfcontactpage_email'])) {
                sendmail($webmaster_email, $subject, $m, $email);
            } else {
                sendmail($modSettings['smfcontactpage_email'], $subject, $m, $email);
            }
            // Show template that mail was sent
            loadtemplate('Contact2');
            // Load the main contact template
            $context['sub_template'] = 'send';
            // Set the page title
            $context['page_title'] = $mbname . $txt['smfcontact_titlesent'];
        }
    } else {
        // Load the main Contact template
        loadtemplate('Contact2');
        // Language strings
        loadLanguage('Login');
        // Load the main Contact template
        $context['sub_template'] = 'main';
        // Set the page title
        $context['page_title'] = $mbname . ' - ' . $txt['smfcontact_contact'];
        // Do we need to show the visual verification image?
        $context['require_verification'] = !empty($modSettings['reg_verification']) && $context['user']['is_guest'] == true;
        if ($context['require_verification']) {
            require_once $sourcedir . '/Subs-Editor.php';
            $verificationOptions = array('id' => 'post');
            $context['require_verification'] = create_control_verification($verificationOptions);
            $context['visual_verification_id'] = $verificationOptions['id'];
        }
    }
}
function setCaptchaError()
{
    global $sourcedir, $context;
    require_once $sourcedir . '/Subs-Editor.php';
    $verificationOptions = array('id' => 'post');
    $context['require_verification'] = create_control_verification($verificationOptions, true);
    if (is_array($context['require_verification'])) {
        fatal_lang_error('adkfatal_captcha_invalid', false);
    }
}
Beispiel #8
0
function PlushSearch2()
{
    global $scripturl, $modSettings, $sourcedir, $txt, $db_connection;
    global $user_info, $context, $options, $messages_request, $boards_can;
    global $excludedWords, $participants, $smcFunc, $search_versions, $searchAPI;
    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, they'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'];
    // Maximum length of the string.
    $context['search_string_limit'] = 100;
    loadLanguage('Search');
    if (!isset($_REQUEST['xml'])) {
        loadTemplate('Search');
    } else {
        $context['sub_template'] = 'results';
    }
    // Are you allowed?
    isAllowedTo('search_posts');
    require_once $sourcedir . '/Display.php';
    require_once $sourcedir . '/Subs-Package.php';
    // Search has a special database set.
    db_extend('search');
    // Load up the search API we are going to use.
    $modSettings['search_index'] = empty($modSettings['search_index']) ? 'standard' : $modSettings['search_index'];
    if (!file_exists($sourcedir . '/SearchAPI-' . ucwords($modSettings['search_index']) . '.php')) {
        fatal_lang_error('search_api_missing');
    }
    loadClassFile('SearchAPI-' . ucwords($modSettings['search_index']) . '.php');
    // Create an instance of the search API and check it is valid for this version of SMF.
    $search_class_name = $modSettings['search_index'] . '_search';
    $searchAPI = new $search_class_name();
    if (!$searchAPI || $searchAPI->supportsMethod('isValid') && !$searchAPI->isValid() || !matchPackageVersion($search_versions['forum_version'], $searchAPI->min_smf_version . '-' . $searchAPI->version_compatible)) {
        // Log the error.
        loadLanguage('Errors');
        log_error(sprintf($txt['search_api_not_compatible'], 'SearchAPI-' . ucwords($modSettings['search_index']) . '.php'), 'critical');
        loadClassFile('SearchAPI-Standard.php');
        $searchAPI = new standard_search();
    }
    // $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'])) {
        // Due to IE's 2083 character limit, we have to compress long search strings
        $temp_params = base64_decode(str_replace(array('-', '_', '.'), array('+', '/', '='), $_REQUEST['params']));
        // Test for gzuncompress failing
        $temp_params2 = @gzuncompress($temp_params);
        $temp_params = explode('|"|', !empty($temp_params2) ? $temp_params2 : $temp_params);
        foreach ($temp_params as $i => $data) {
            @(list($k, $v) = explode('|\'|', $data));
            $search_params[$k] = $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 = $smcFunc['db_query']('', '
			SELECT ' . (empty($search_params['maxage']) ? '0, ' : 'IFNULL(MIN(id_msg), -1), ') . (empty($search_params['minage']) ? '0' : 'IFNULL(MAX(id_msg), -1)') . '
			FROM {db_prefix}messages
			WHERE 1=1' . ($modSettings['postmod_active'] ? '
				AND approved = {int:is_approved_true}' : '') . (empty($search_params['minage']) ? '' : '
				AND poster_time <= {int:timestamp_minimum_age}') . (empty($search_params['maxage']) ? '' : '
				AND poster_time >= {int:timestamp_maximum_age}'), array('timestamp_minimum_age' => empty($search_params['minage']) ? 0 : time() - 86400 * $search_params['minage'], 'timestamp_maximum_age' => empty($search_params['maxage']) ? 0 : time() - 86400 * $search_params['maxage'], 'is_approved_true' => 1));
        list($minMsgID, $maxMsgID) = $smcFunc['db_fetch_row']($request);
        if ($minMsgID < 0 || $maxMsgID < 0) {
            $context['search_errors']['no_messages_in_time_frame'] = true;
        }
        $smcFunc['db_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($smcFunc['htmlspecialchars']($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]);
            }
        }
        // Create a list of database-escaped search names.
        $realNameMatches = array();
        foreach ($possible_users as $possible_user) {
            $realNameMatches[] = $smcFunc['db_quote']('{string:possible_user}', array('possible_user' => $possible_user));
        }
        // Retrieve a list of possible members.
        $request = $smcFunc['db_query']('', '
			SELECT id_member
			FROM {db_prefix}members
			WHERE {raw:match_possible_users}', array('match_possible_users' => 'real_name LIKE ' . implode(' OR real_name LIKE ', $realNameMatches)));
        // Simply do nothing if there're too many members matching the criteria.
        if ($smcFunc['db_num_rows']($request) > $maxMembersToSearch) {
            $userQuery = '';
        } elseif ($smcFunc['db_num_rows']($request) == 0) {
            $userQuery = $smcFunc['db_quote']('m.id_member = {int:id_member_guest} AND ({raw:match_possible_guest_names})', array('id_member_guest' => 0, 'match_possible_guest_names' => 'm.poster_name LIKE ' . implode(' OR m.poster_name LIKE ', $realNameMatches)));
        } else {
            $memberlist = array();
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                $memberlist[] = $row['id_member'];
            }
            $userQuery = $smcFunc['db_quote']('(m.id_member IN ({array_int:matched_members}) OR (m.id_member = {int:id_member_guest} AND ({raw:match_possible_guest_names})))', array('matched_members' => $memberlist, 'id_member_guest' => 0, 'match_possible_guest_names' => 'm.poster_name LIKE ' . implode(' OR m.poster_name LIKE ', $realNameMatches)));
        }
        $smcFunc['db_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 = $smcFunc['db_query']('', '
			SELECT b.id_board
			FROM {db_prefix}topics AS t
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
			WHERE t.id_topic = {int:search_topic_id}
				AND {query_see_board}' . ($modSettings['postmod_active'] ? '
				AND t.approved = {int:is_approved_true}' : '') . '
			LIMIT 1', array('search_topic_id' => $search_params['topic'], 'is_approved_true' => 1));
        if ($smcFunc['db_num_rows']($request) == 0) {
            fatal_lang_error('topic_gone', false);
        }
        $search_params['brd'] = array();
        list($search_params['brd'][0]) = $smcFunc['db_fetch_row']($request);
        $smcFunc['db_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 {
        $see_board = empty($search_params['advanced']) ? 'query_wanna_see_board' : 'query_see_board';
        $request = $smcFunc['db_query']('', '
			SELECT b.id_board
			FROM {db_prefix}boards AS b
			WHERE {raw:boards_allowed_to_see}
				AND redirect = {string:empty_string}' . (empty($_REQUEST['brd']) ? !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
				AND b.id_board != {int:recycle_board_id}' : '' : '
				AND b.id_board IN ({array_int:selected_search_boards})'), array('boards_allowed_to_see' => $user_info[$see_board], 'empty_string' => '', 'selected_search_boards' => empty($_REQUEST['brd']) ? array() : $_REQUEST['brd'], 'recycle_board_id' => $modSettings['recycle_board']));
        $search_params['brd'] = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $search_params['brd'][] = $row['id_board'];
        }
        $smcFunc['db_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) {
        foreach ($search_params['brd'] as $k => $v) {
            $search_params['brd'][$k] = (int) $v;
        }
        // If we've selected all boards, this parameter can be left empty.
        $request = $smcFunc['db_query']('', '
			SELECT COUNT(*)
			FROM {db_prefix}boards
			WHERE redirect = {string:empty_string}', array('empty_string' => ''));
        list($num_boards) = $smcFunc['db_fetch_row']($request);
        $smcFunc['db_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', 'num_replies', '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'] === 'num_replies') {
        $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'] = $_POST['search'];
        } else {
            $search_params['search'] = '';
        }
    }
    // Nothing??
    if (!isset($search_params['search']) || $search_params['search'] == '') {
        $context['search_errors']['invalid_search_string'] = true;
    } elseif ($smcFunc['strlen']($search_params['search']) > $context['search_string_limit']) {
        $context['search_errors']['string_too_long'] = true;
        $txt['error_string_too_long'] = sprintf($txt['error_string_too_long'], $context['search_string_limit']);
    }
    // Change non-word characters into spaces.
    $stripped_query = preg_replace('~(?:[\\x0B\\0' . ($context['utf8'] ? $context['server']['complex_preg_chars'] ? '\\x{A0}' : " " : '\\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($smcFunc['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' : ''), ' ', $search_params['search']));
    // 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[] = $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[] = $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) {
        // Skip anything practically empty.
        if (($searchArray[$index] = trim($value, '-_\' ')) === '') {
            unset($searchArray[$index]);
        } elseif (in_array($searchArray[$index], $blacklisted_words)) {
            $foundBlackListedWords = true;
            unset($searchArray[$index]);
        } elseif ($smcFunc['strlen']($value) < 2) {
            $context['search_errors']['search_string_small_words'] = true;
            unset($searchArray[$index]);
        } else {
            $searchArray[$index] = $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] = '<strong class="highlight">' . $word . '</strong>';
    }
    // 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' . (!empty($foundBlackListedWords) ? '_blacklist' : '')] = true;
    } elseif (empty($search_params['searchtype'])) {
        $orParts[0] = $searchArray;
    } else {
        foreach ($searchArray as $index => $value) {
            $orParts[$index] = array($value);
        }
    }
    // Don't allow duplicate error messages if one string is too short.
    if (isset($context['search_errors']['search_string_small_words'], $context['search_errors']['invalid_search_string'])) {
        unset($context['search_errors']['invalid_search_string']);
    }
    // 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 ($searchAPI->supportsMethod('searchSort')) {
            usort($orParts[$orIndex], 'searchSort');
        }
        foreach ($orParts[$orIndex] as $word) {
            $is_excluded = in_array($word, $excludedWords);
            $searchWords[$orIndex]['all_words'][] = $word;
            $subjectWords = text2words($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;
            }
            // Have we got indexes to prepare?
            if ($searchAPI->supportsMethod('prepareIndexes')) {
                $searchAPI->prepareIndexes($word, $searchWords[$orIndex], $excludedIndexWords, $is_excluded);
            }
        }
        // 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;
        } elseif ($search_params['subject_only'] && empty($searchWords[$orIndex]['subject_words']) && empty($excludedSubjectWords)) {
            $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);
        if (!$pspell_link) {
            $pspell_link = pspell_new('en', '', '', '', PSPELL_FAST | PSPELL_RUN_TOGETHER);
        }
        error_reporting($old);
        ob_end_clean();
        $did_you_mean = array('search' => array(), 'display' => array());
        $found_misspelling = false;
        foreach ($searchArray as $word) {
            if (empty($pspell_link)) {
                continue;
            }
            $word = $word;
            // Don't check phrases.
            if (preg_match('~^\\w+$~', $word) === 0) {
                $did_you_mean['search'][] = '"' . $word . '"';
                $did_you_mean['display'][] = '&quot;' . $smcFunc['htmlspecialchars']($word) . '&quot;';
                continue;
            } elseif (preg_match('~\\d~', $word) === 1) {
                $did_you_mean['search'][] = $word;
                $did_you_mean['display'][] = $smcFunc['htmlspecialchars']($word);
                continue;
            } elseif (pspell_check($pspell_link, $word)) {
                $did_you_mean['search'][] = $word;
                $did_you_mean['display'][] = $smcFunc['htmlspecialchars']($word);
                continue;
            }
            $suggestions = pspell_suggest($pspell_link, $word);
            foreach ($suggestions as $i => $s) {
                // Search is case insensitive.
                if ($smcFunc['strtolower']($s) == $smcFunc['strtolower']($word)) {
                    unset($suggestions[$i]);
                } elseif ($suggestions[$i] != censorText($s)) {
                    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><strong>' . $smcFunc['htmlspecialchars']($suggestions[0]) . '</strong></em>';
                $found_misspelling = true;
            } else {
                $did_you_mean['search'][] = $word;
                $did_you_mean['display'][] = $smcFunc['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 = $word;
                if (preg_match('~^\\w+$~', $word) == 0) {
                    $temp_excluded['search'][] = '-"' . $word . '"';
                    $temp_excluded['display'][] = '-&quot;' . $smcFunc['htmlspecialchars']($word) . '&quot;';
                } else {
                    $temp_excluded['search'][] = '-' . $word;
                    $temp_excluded['display'][] = '-' . $smcFunc['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 . '|\'|' . $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'] = $smcFunc['htmlspecialchars']($context['search_params']['search']);
    }
    if (isset($context['search_params']['userspec'])) {
        $context['search_params']['userspec'] = $smcFunc['htmlspecialchars']($context['search_params']['userspec']);
    }
    // Do we have captcha enabled?
    if ($user_info['is_guest'] && !empty($modSettings['search_enable_captcha']) && empty($_SESSION['ss_vv_passed']) && (empty($_SESSION['last_ss']) || $_SESSION['last_ss'] != $search_params['search'])) {
        // If we come from another search box tone down the error...
        if (!isset($_REQUEST['search_vv'])) {
            $context['search_errors']['need_verification_code'] = true;
        } else {
            require_once $sourcedir . '/Subs-Editor.php';
            $verificationOptions = array('id' => 'search');
            $context['require_verification'] = create_control_verification($verificationOptions, true);
            if (is_array($context['require_verification'])) {
                foreach ($context['require_verification'] as $error) {
                    $context['search_errors'][$error] = true;
                }
            } else {
                $_SESSION['ss_vv_passed'] = true;
            }
        }
    }
    // *** 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 . '|\'|' . $v;
    }
    if (!empty($context['params'])) {
        // Due to old IE's 2083 character limit, we have to compress long search strings
        $params = @gzcompress(implode('|"|', $context['params']));
        // Gzcompress failed, use try non-gz
        if (empty($params)) {
            $params = implode('|"|', $context['params']);
        }
        // Base64 encode, then replace +/= with uri safe ones that can be reverted
        $context['params'] = str_replace(array('+', '/', '='), array('-', '_', '.'), base64_encode($params));
    }
    // ... and add the links to the link tree.
    $context['linktree'][] = array('url' => $scripturl . '?action=search;params=' . $context['params'], 'name' => $txt['search']);
    $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();
    }
    // Spam me not, Spam-a-lot?
    if (empty($_SESSION['last_ss']) || $_SESSION['last_ss'] != $search_params['search']) {
        spamProtection('search');
    }
    // Store the last search string to allow pages of results to be browsed.
    $_SESSION['last_ss'] = $search_params['search'];
    // *** Reserve an ID for caching the search results.
    $query_params = array_merge($search_params, array('min_msg_id' => isset($minMsgID) ? (int) $minMsgID : 0, 'max_msg_id' => isset($maxMsgID) ? (int) $maxMsgID : 0, 'memberlist' => !empty($memberlist) ? $memberlist : array()));
    // Can this search rely on the API given the parameters?
    if ($searchAPI->supportsMethod('searchQuery', $query_params)) {
        $participants = array();
        $searchArray = array();
        $num_results = $searchAPI->searchQuery($query_params, $searchWords, $excludedIndexWords, $participants, $searchArray);
    } else {
        $update_cache = empty($_SESSION['search_cache']) || $_SESSION['search_cache']['params'] != $context['params'];
        if ($update_cache) {
            // 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.
            $smcFunc['db_search_query']('delete_log_search_results', '
				DELETE FROM {db_prefix}log_search_results
				WHERE id_search = {int:search_id}', array('search_id' => $_SESSION['search_cache']['id_search']));
            if ($search_params['subject_only']) {
                // We do this to try and avoid duplicate keys on databases not supporting INSERT IGNORE.
                $inserts = array();
                foreach ($searchWords as $orIndex => $words) {
                    $subject_query_params = array();
                    $subject_query = array('from' => '{db_prefix}topics AS t', 'inner_join' => array(), 'left_join' => array(), 'where' => array());
                    if ($modSettings['postmod_active']) {
                        $subject_query['where'][] = 't.approved = {int:is_approved}';
                    }
                    $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 {string:subject_words_' . $numTables . '_wild}' : '= {string:subject_words_' . $numTables . '}') . ' AND subj' . $numTables . '.id_topic = t.id_topic)';
                            $subject_query['where'][] = '(subj' . $numTables . '.word IS NULL)';
                        } else {
                            $subject_query['inner_join'][] = '{db_prefix}log_search_subjects AS subj' . $numTables . ' ON (subj' . $numTables . '.id_topic = ' . ($prev_join === 0 ? 't' : 'subj' . $prev_join) . '.id_topic)';
                            $subject_query['where'][] = 'subj' . $numTables . '.word ' . (empty($modSettings['search_match_words']) ? 'LIKE {string:subject_words_' . $numTables . '_wild}' : '= {string:subject_words_' . $numTables . '}');
                            $prev_join = $numTables;
                        }
                        $subject_query_params['subject_words_' . $numTables] = $subjectWord;
                        $subject_query_params['subject_words_' . $numTables . '_wild'] = '%' . $subjectWord . '%';
                    }
                    if (!empty($userQuery)) {
                        if ($subject_query['from'] != '{db_prefix}messages AS m') {
                            $subject_query['inner_join'][] = '{db_prefix}messages AS m ON (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 ($subject_query['from'] != '{db_prefix}messages AS m') {
                            $subject_query['inner_join'][] = '{db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)';
                        }
                        $count = 0;
                        foreach ($excludedPhrases as $phrase) {
                            $subject_query['where'][] = 'm.subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : ' RLIKE ') . '{string:excluded_phrases_' . $count . '}';
                            $subject_query_params['excluded_phrases_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . '[[:>:]]';
                        }
                    }
                    $ignoreRequest = $smcFunc['db_search_query']('insert_log_search_results_subject', ($smcFunc['db_support_ignore'] ? '
						INSERT IGNORE INTO {db_prefix}log_search_results
							(id_search, id_topic, relevance, id_msg, num_matches)' : '') . '
						SELECT
							{int:id_search},
							t.id_topic,
							1000 * (
								{int:weight_frequency} / (t.num_replies + 1) +
								{int:weight_age} * CASE WHEN t.id_first_msg < {int:min_msg} THEN 0 ELSE (t.id_first_msg - {int:min_msg}) / {int:recent_message} END +
								{int:weight_length} * CASE WHEN t.num_replies < {int:huge_topic_posts} THEN t.num_replies / {int:huge_topic_posts} ELSE 1 END +
								{int:weight_subject} +
								{int:weight_sticky} * t.is_sticky
							) / {int:weight_total} AS relevance,
							' . (empty($userQuery) ? 't.id_first_msg' : 'm.id_msg') . ',
							1
						FROM ' . $subject_query['from'] . (empty($subject_query['inner_join']) ? '' : '
							INNER JOIN ' . implode('
							INNER JOIN ', $subject_query['inner_join'])) . (empty($subject_query['left_join']) ? '' : '
							LEFT JOIN ' . implode('
							LEFT JOIN ', $subject_query['left_join'])) . '
						WHERE ' . implode('
							AND ', $subject_query['where']) . (empty($modSettings['search_max_results']) ? '' : '
						LIMIT ' . ($modSettings['search_max_results'] - $numSubjectResults)), array_merge($subject_query_params, array('id_search' => $_SESSION['search_cache']['id_search'], 'weight_age' => $weight['age'], 'weight_frequency' => $weight['frequency'], 'weight_length' => $weight['length'], 'weight_sticky' => $weight['sticky'], 'weight_subject' => $weight['subject'], 'weight_total' => $weight_total, 'min_msg' => $minMsg, 'recent_message' => $recentMsg, 'huge_topic_posts' => $humungousTopicPosts, 'is_approved' => 1)));
                    // If the database doesn't support IGNORE to make this fast we need to do some tracking.
                    if (!$smcFunc['db_support_ignore']) {
                        while ($row = $smcFunc['db_fetch_row']($ignoreRequest)) {
                            // No duplicates!
                            if (isset($inserts[$row[1]])) {
                                continue;
                            }
                            foreach ($row as $key => $value) {
                                $inserts[$row[1]][] = (int) $row[$key];
                            }
                        }
                        $smcFunc['db_free_result']($ignoreRequest);
                        $numSubjectResults = count($inserts);
                    } else {
                        $numSubjectResults += $smcFunc['db_affected_rows']();
                    }
                    if (!empty($modSettings['search_max_results']) && $numSubjectResults >= $modSettings['search_max_results']) {
                        break;
                    }
                }
                // If there's data to be inserted for non-IGNORE databases do it here!
                if (!empty($inserts)) {
                    $smcFunc['db_insert']('', '{db_prefix}log_search_results', array('id_search' => 'int', 'id_topic' => 'int', 'relevance' => 'int', 'id_msg' => 'int', 'num_matches' => 'int'), $inserts, array('id_search', 'id_topic'));
                }
                $_SESSION['search_cache']['num_results'] = $numSubjectResults;
            } else {
                $main_query = array('select' => array('id_search' => $_SESSION['search_cache']['id_search'], 'relevance' => '0'), 'weights' => array(), 'from' => '{db_prefix}topics AS t', 'inner_join' => array('{db_prefix}messages AS m ON (m.id_topic = t.id_topic)'), 'left_join' => array(), 'where' => array(), 'group_by' => array(), 'parameters' => array('min_msg' => $minMsg, 'recent_message' => $recentMsg, 'huge_topic_posts' => $humungousTopicPosts, 'is_approved' => 1));
                if (empty($search_params['topic']) && empty($search_params['show_complete'])) {
                    $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(*) / (MAX(t.num_replies) + 1)', 'age' => 'CASE WHEN MAX(m.id_msg) < {int:min_msg} THEN 0 ELSE (MAX(m.id_msg) - {int:min_msg}) / {int:recent_message} END', 'length' => 'CASE WHEN MAX(t.num_replies) < {int:huge_topic_posts} THEN MAX(t.num_replies) / {int:huge_topic_posts} ELSE 1 END', 'subject' => '0', 'first_message' => 'CASE WHEN MIN(m.id_msg) = MAX(t.id_first_msg) THEN 1 ELSE 0 END', 'sticky' => 'MAX(t.is_sticky)');
                    $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) / CASE WHEN t.id_last_msg = t.id_first_msg THEN 1 ELSE t.id_last_msg - t.id_first_msg END)', 'first_message' => 'CASE WHEN m.id_msg = t.id_first_msg THEN 1 ELSE 0 END');
                    if (!empty($search_params['topic'])) {
                        $main_query['where'][] = 't.id_topic = {int:topic}';
                        $main_query['parameters']['topic'] = $search_params['topic'];
                    }
                    if (!empty($search_params['show_complete'])) {
                        $main_query['group_by'][] = 'm.id_msg, t.id_first_msg, t.id_last_msg';
                    }
                }
                // *** Get the subject results.
                $numSubjectResults = 0;
                if (empty($search_params['topic'])) {
                    $inserts = array();
                    // Create a temporary table to store some preliminary results in.
                    $smcFunc['db_search_query']('drop_tmp_log_search_topics', '
						DROP TABLE IF EXISTS {db_prefix}tmp_log_search_topics', array('db_error_skip' => true));
                    $createTemporary = $smcFunc['db_search_query']('create_tmp_log_search_topics', '
						CREATE TEMPORARY TABLE {db_prefix}tmp_log_search_topics (
							id_topic mediumint(8) unsigned NOT NULL default {string:string_zero},
							PRIMARY KEY (id_topic)
						) ENGINE=MEMORY', array('string_zero' => '0', 'db_error_skip' => true)) !== false;
                    // Clean up some previous cache.
                    if (!$createTemporary) {
                        $smcFunc['db_search_query']('delete_log_search_topics', '
							DELETE FROM {db_prefix}log_search_topics
							WHERE id_search = {int:search_id}', array('search_id' => $_SESSION['search_cache']['id_search']));
                    }
                    foreach ($searchWords as $orIndex => $words) {
                        $subject_query = array('from' => '{db_prefix}topics AS t', 'inner_join' => array(), 'left_join' => array(), 'where' => array(), 'params' => array());
                        $numTables = 0;
                        $prev_join = 0;
                        $count = 0;
                        foreach ($words['subject_words'] as $subjectWord) {
                            $numTables++;
                            if (in_array($subjectWord, $excludedSubjectWords)) {
                                if ($subject_query['from'] != '{db_prefix}messages AS m') {
                                    $subject_query['inner_join'][] = '{db_prefix}messages AS m ON (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 {string:subject_not_' . $count . '}' : '= {string:subject_not_' . $count . '}') . ' AND subj' . $numTables . '.id_topic = t.id_topic)';
                                $subject_query['params']['subject_not_' . $count] = empty($modSettings['search_match_words']) ? '%' . $subjectWord . '%' : $subjectWord;
                                $subject_query['where'][] = '(subj' . $numTables . '.word IS NULL)';
                                $subject_query['where'][] = 'm.body NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : ' RLIKE ') . '{string:body_not_' . $count . '}';
                                $subject_query['params']['body_not_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($subjectWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $subjectWord), '\\\'') . '[[:>:]]';
                            } else {
                                $subject_query['inner_join'][] = '{db_prefix}log_search_subjects AS subj' . $numTables . ' ON (subj' . $numTables . '.id_topic = ' . ($prev_join === 0 ? 't' : 'subj' . $prev_join) . '.id_topic)';
                                $subject_query['where'][] = 'subj' . $numTables . '.word LIKE {string:subject_like_' . $count . '}';
                                $subject_query['params']['subject_like_' . $count++] = empty($modSettings['search_match_words']) ? '%' . $subjectWord . '%' : $subjectWord;
                                $prev_join = $numTables;
                            }
                        }
                        if (!empty($userQuery)) {
                            if ($subject_query['from'] != '{db_prefix}messages AS m') {
                                $subject_query['inner_join'][] = '{db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)';
                            }
                            $subject_query['where'][] = '{raw:user_query}';
                            $subject_query['params']['user_query'] = $userQuery;
                        }
                        if (!empty($search_params['topic'])) {
                            $subject_query['where'][] = 't.id_topic = {int:topic}';
                            $subject_query['params']['topic'] = $search_params['topic'];
                        }
                        if (!empty($minMsgID)) {
                            $subject_query['where'][] = 't.id_first_msg >= {int:min_msg_id}';
                            $subject_query['params']['min_msg_id'] = $minMsgID;
                        }
                        if (!empty($maxMsgID)) {
                            $subject_query['where'][] = 't.id_last_msg <= {int:max_msg_id}';
                            $subject_query['params']['max_msg_id'] = $maxMsgID;
                        }
                        if (!empty($boardQuery)) {
                            $subject_query['where'][] = 't.id_board {raw:board_query}';
                            $subject_query['params']['board_query'] = $boardQuery;
                        }
                        if (!empty($excludedPhrases)) {
                            if ($subject_query['from'] != '{db_prefix}messages AS m') {
                                $subject_query['inner_join'][] = '{db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)';
                            }
                            $count = 0;
                            foreach ($excludedPhrases as $phrase) {
                                $subject_query['where'][] = 'm.subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : ' RLIKE ') . '{string:exclude_phrase_' . $count . '}';
                                $subject_query['where'][] = 'm.body NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : ' RLIKE ') . '{string:exclude_phrase_' . $count . '}';
                                $subject_query['params']['exclude_phrase_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\\[\\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . '[[:>:]]';
                            }
                        }
                        // Nothing to search for?
                        if (empty($subject_query['where'])) {
                            continue;
                        }
                        $ignoreRequest = $smcFunc['db_search_query']('insert_log_search_topics', ($smcFunc['db_support_ignore'] ? '
							INSERT IGNORE INTO {db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_topics
								(' . ($createTemporary ? '' : 'id_search, ') . 'id_topic)' : '') . '
							SELECT ' . ($createTemporary ? '' : $_SESSION['search_cache']['id_search'] . ', ') . 't.id_topic
							FROM ' . $subject_query['from'] . (empty($subject_query['inner_join']) ? '' : '
								INNER JOIN ' . implode('
								INNER JOIN ', $subject_query['inner_join'])) . (empty($subject_query['left_join']) ? '' : '
								LEFT JOIN ' . implode('
								LEFT JOIN ', $subject_query['left_join'])) . '
							WHERE ' . implode('
								AND ', $subject_query['where']) . (empty($modSettings['search_max_results']) ? '' : '
							LIMIT ' . ($modSettings['search_max_results'] - $numSubjectResults)), $subject_query['params']);
                        // Don't do INSERT IGNORE? Manually fix this up!
                        if (!$smcFunc['db_support_ignore']) {
                            while ($row = $smcFunc['db_fetch_row']($ignoreRequest)) {
                                $ind = $createTemporary ? 0 : 1;
                                // No duplicates!
                                if (isset($inserts[$row[$ind]])) {
                                    continue;
                                }
                                $inserts[$row[$ind]] = $row;
                            }
                            $smcFunc['db_free_result']($ignoreRequest);
                            $numSubjectResults = count($inserts);
                        } else {
                            $numSubjectResults += $smcFunc['db_affected_rows']();
                        }
                        if (!empty($modSettings['search_max_results']) && $numSubjectResults >= $modSettings['search_max_results']) {
                            break;
                        }
                    }
                    // Got some non-MySQL data to plonk in?
                    if (!empty($inserts)) {
                        $smcFunc['db_insert']('', '{db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_topics', $createTemporary ? array('id_topic' => 'int') : array('id_search' => 'int', 'id_topic' => 'int'), $inserts, $createTemporary ? array('id_topic') : array('id_search', 'id_topic'));
                    }
                    if ($numSubjectResults !== 0) {
                        $main_query['weights']['subject'] = 'CASE WHEN MAX(lst.id_topic) IS NULL THEN 0 ELSE 1 END';
                        $main_query['left_join'][] = '{db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_topics AS lst ON (' . ($createTemporary ? '' : 'lst.id_search = {int:id_search} AND ') . 'lst.id_topic = t.id_topic)';
                        if (!$createTemporary) {
                            $main_query['parameters']['id_search'] = $_SESSION['search_cache']['id_search'];
                        }
                    }
                }
                $indexedResults = 0;
                // We building an index?
                if ($searchAPI->supportsMethod('indexedWordQuery', $query_params)) {
                    $inserts = array();
                    $smcFunc['db_search_query']('drop_tmp_log_search_messages', '
						DROP TABLE IF EXISTS {db_prefix}tmp_log_search_messages', array('db_error_skip' => true));
                    $createTemporary = $smcFunc['db_search_query']('create_tmp_log_search_messages', '
						CREATE TEMPORARY TABLE {db_prefix}tmp_log_search_messages (
							id_msg int(10) unsigned NOT NULL default {string:string_zero},
							PRIMARY KEY (id_msg)
						) ENGINE=MEMORY', array('string_zero' => '0', 'db_error_skip' => true)) !== false;
                    // Clear, all clear!
                    if (!$createTemporary) {
                        $smcFunc['db_search_query']('delete_log_search_messages', '
							DELETE FROM {db_prefix}log_search_messages
							WHERE id_search = {int:id_search}', array('id_search' => $_SESSION['search_cache']['id_search']));
                    }
                    foreach ($searchWords as $orIndex => $words) {
                        // Search for this word, assuming we have some words!
                        if (!empty($words['indexed_words'])) {
                            // Variables required for the search.
                            $search_data = array('insert_into' => ($createTemporary ? 'tmp_' : '') . 'log_search_messages', 'no_regexp' => $no_regexp, 'max_results' => $maxMessageResults, 'indexed_results' => $indexedResults, 'params' => array('id_search' => !$createTemporary ? $_SESSION['search_cache']['id_search'] : 0, 'excluded_words' => $excludedWords, 'user_query' => !empty($userQuery) ? $userQuery : '', 'board_query' => !empty($boardQuery) ? $boardQuery : '', 'topic' => !empty($search_params['topic']) ? $search_params['topic'] : 0, 'min_msg_id' => !empty($minMsgID) ? $minMsgID : 0, 'max_msg_id' => !empty($maxMsgID) ? $maxMsgID : 0, 'excluded_phrases' => !empty($excludedPhrases) ? $excludedPhrases : array(), 'excluded_index_words' => !empty($excludedIndexWords) ? $excludedIndexWords : array(), 'excluded_subject_words' => !empty($excludedSubjectWords) ? $excludedSubjectWords : array()));
                            $ignoreRequest = $searchAPI->indexedWordQuery($words, $search_data);
                            if (!$smcFunc['db_support_ignore']) {
                                while ($row = $smcFunc['db_fetch_row']($ignoreRequest)) {
                                    // No duplicates!
                                    if (isset($inserts[$row[0]])) {
                                        continue;
                                    }
                                    $inserts[$row[0]] = $row;
                                }
                                $smcFunc['db_free_result']($ignoreRequest);
                                $indexedResults = count($inserts);
                            } else {
                                $indexedResults += $smcFunc['db_affected_rows']();
                            }
                            if (!empty($maxMessageResults) && $indexedResults >= $maxMessageResults) {
                                break;
                            }
                        }
                    }
                    // More non-MySQL stuff needed?
                    if (!empty($inserts)) {
                        $smcFunc['db_insert']('', '{db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_messages', $createTemporary ? array('id_msg' => 'int') : array('id_msg' => 'int', 'id_search' => 'int'), $inserts, $createTemporary ? array('id_msg') : array('id_msg', 'id_search'));
                    }
                    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['inner_join'][] = '{db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_messages AS lsm ON (lsm.id_msg = m.id_msg)';
                        if (!$createTemporary) {
                            $main_query['where'][] = 'lsm.id_search = {int:id_search}';
                            $main_query['parameters']['id_search'] = $_SESSION['search_cache']['id_search'];
                        }
                    }
                } else {
                    $orWhere = array();
                    $count = 0;
                    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 ' : ' RLIKE ') . '{string:all_word_body_' . $count . '}';
                            if (in_array($regularWord, $excludedWords)) {
                                $where[] = 'm.subject NOT' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : ' RLIKE ') . '{string:all_word_body_' . $count . '}';
                            }
                            $main_query['parameters']['all_word_body_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . 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'][] = '{raw:user_query}';
                        $main_query['parameters']['user_query'] = $userQuery;
                    }
                    if (!empty($search_params['topic'])) {
                        $main_query['where'][] = 'm.id_topic = {int:topic}';
                        $main_query['parameters']['topic'] = $search_params['topic'];
                    }
                    if (!empty($minMsgID)) {
                        $main_query['where'][] = 'm.id_msg >= {int:min_msg_id}';
                        $main_query['parameters']['min_msg_id'] = $minMsgID;
                    }
                    if (!empty($maxMsgID)) {
                        $main_query['where'][] = 'm.id_msg <= {int:max_msg_id}';
                        $main_query['parameters']['max_msg_id'] = $maxMsgID;
                    }
                    if (!empty($boardQuery)) {
                        $main_query['where'][] = 'm.id_board {raw:board_query}';
                        $main_query['parameters']['board_query'] = $boardQuery;
                    }
                }
                // Did we either get some indexed results, or otherwise did not do an indexed query?
                if (!empty($indexedResults) || !$searchAPI->supportsMethod('indexedWordQuery', $query_params)) {
                    $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';
                    $ignoreRequest = $smcFunc['db_search_query']('insert_log_search_results_no_index', ($smcFunc['db_support_ignore'] ? '
						INSERT IGNORE INTO ' . '{db_prefix}log_search_results
							(' . implode(', ', array_keys($main_query['select'])) . ')' : '') . '
						SELECT
							' . implode(',
							', $main_query['select']) . '
						FROM ' . $main_query['from'] . (empty($main_query['inner_join']) ? '' : '
							INNER JOIN ' . implode('
							INNER JOIN ', $main_query['inner_join'])) . (empty($main_query['left_join']) ? '' : '
							LEFT JOIN ' . implode('
							LEFT JOIN ', $main_query['left_join'])) . (!empty($main_query['where']) ? '
						WHERE ' : '') . implode('
							AND ', $main_query['where']) . (empty($main_query['group_by']) ? '' : '
						GROUP BY ' . implode(', ', $main_query['group_by'])) . (empty($modSettings['search_max_results']) ? '' : '
						LIMIT ' . $modSettings['search_max_results']), $main_query['parameters']);
                    // We love to handle non-good databases that don't support our ignore!
                    if (!$smcFunc['db_support_ignore']) {
                        $inserts = array();
                        while ($row = $smcFunc['db_fetch_row']($ignoreRequest)) {
                            // No duplicates!
                            if (isset($inserts[$row[2]])) {
                                continue;
                            }
                            foreach ($row as $key => $value) {
                                $inserts[$row[2]][] = (int) $row[$key];
                            }
                        }
                        $smcFunc['db_free_result']($ignoreRequest);
                        // Now put them in!
                        if (!empty($inserts)) {
                            $query_columns = array();
                            foreach ($main_query['select'] as $k => $v) {
                                $query_columns[$k] = 'int';
                            }
                            $smcFunc['db_insert']('', '{db_prefix}log_search_results', $query_columns, $inserts, array('id_search', 'id_topic'));
                        }
                        $_SESSION['search_cache']['num_results'] += count($inserts);
                    } else {
                        $_SESSION['search_cache']['num_results'] = $smcFunc['db_affected_rows']();
                    }
                }
                // Insert subject-only matches.
                if ($_SESSION['search_cache']['num_results'] < $modSettings['search_max_results'] && $numSubjectResults !== 0) {
                    $usedIDs = array_flip(empty($inserts) ? array() : array_keys($inserts));
                    $ignoreRequest = $smcFunc['db_search_query']('insert_log_search_results_sub_only', ($smcFunc['db_support_ignore'] ? '
						INSERT IGNORE INTO {db_prefix}log_search_results
							(id_search, id_topic, relevance, id_msg, num_matches)' : '') . '
						SELECT
							{int:id_search},
							t.id_topic,
							1000 * (
								{int:weight_frequency} / (t.num_replies + 1) +
								{int:weight_age} * CASE WHEN t.id_first_msg < {int:min_msg} THEN 0 ELSE (t.id_first_msg - {int:min_msg}) / {int:recent_message} END +
								{int:weight_length} * CASE WHEN t.num_replies < {int:huge_topic_posts} THEN t.num_replies / {int:huge_topic_posts} ELSE 1 END +
								{int:weight_subject} +
								{int:weight_sticky} * t.is_sticky
							) / {int:weight_total} AS relevance,
							t.id_first_msg,
							1
						FROM {db_prefix}topics AS t
							INNER JOIN {db_prefix}' . ($createTemporary ? 'tmp_' : '') . 'log_search_topics AS lst ON (lst.id_topic = t.id_topic)' . ($createTemporary ? '' : 'WHERE lst.id_search = {int:id_search}') . (empty($modSettings['search_max_results']) ? '' : '
						LIMIT ' . ($modSettings['search_max_results'] - $_SESSION['search_cache']['num_results'])), array('id_search' => $_SESSION['search_cache']['id_search'], 'weight_age' => $weight['age'], 'weight_frequency' => $weight['frequency'], 'weight_length' => $weight['frequency'], 'weight_sticky' => $weight['frequency'], 'weight_subject' => $weight['frequency'], 'weight_total' => $weight_total, 'min_msg' => $minMsg, 'recent_message' => $recentMsg, 'huge_topic_posts' => $humungousTopicPosts));
                    // Once again need to do the inserts if the database don't support ignore!
                    if (!$smcFunc['db_support_ignore']) {
                        $inserts = array();
                        while ($row = $smcFunc['db_fetch_row']($ignoreRequest)) {
                            // No duplicates!
                            if (isset($usedIDs[$row[1]])) {
                                continue;
                            }
                            $usedIDs[$row[1]] = true;
                            $inserts[] = $row;
                        }
                        $smcFunc['db_free_result']($ignoreRequest);
                        // Now put them in!
                        if (!empty($inserts)) {
                            $smcFunc['db_insert']('', '{db_prefix}log_search_results', array('id_search' => 'int', 'id_topic' => 'int', 'relevance' => 'float', 'id_msg' => 'int', 'num_matches' => 'int'), $inserts, array('id_search', 'id_topic'));
                        }
                        $_SESSION['search_cache']['num_results'] += count($inserts);
                    } else {
                        $_SESSION['search_cache']['num_results'] += $smcFunc['db_affected_rows']();
                    }
                } else {
                    $_SESSION['search_cache']['num_results'] = 0;
                }
            }
        }
        // *** Retrieve the results to be shown on the page
        $participants = array();
        $request = $smcFunc['db_search_query']('', '
			SELECT ' . (empty($search_params['topic']) ? 'lsr.id_topic' : $search_params['topic'] . ' AS id_topic') . ', lsr.id_msg, lsr.relevance, lsr.num_matches
			FROM {db_prefix}log_search_results AS lsr' . ($search_params['sort'] == 'num_replies' ? '
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = lsr.id_topic)' : '') . '
			WHERE lsr.id_search = {int:id_search}
			ORDER BY ' . $search_params['sort'] . ' ' . $search_params['sort_dir'] . '
			LIMIT ' . (int) $_REQUEST['start'] . ', ' . $modSettings['search_results_per_page'], array('id_search' => $_SESSION['search_cache']['id_search']));
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $context['topics'][$row['id_msg']] = array('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;
        }
        $smcFunc['db_free_result']($request);
        $num_results = $_SESSION['search_cache']['num_results'];
    }
    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'])) {
            $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']);
        }
        // What messages are we using?
        $msg_list = array_keys($context['topics']);
        // Load the posters...
        $request = $smcFunc['db_query']('', '
			SELECT id_member
			FROM {db_prefix}messages
			WHERE id_member != {int:no_member}
				AND id_msg IN ({array_int:message_list})
			LIMIT ' . count($context['topics']), array('message_list' => $msg_list, 'no_member' => 0));
        $posters = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            $posters[] = $row['id_member'];
        }
        $smcFunc['db_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 = $smcFunc['db_query']('', '
			SELECT
				m.id_msg, m.subject, m.poster_name, m.poster_email, m.poster_time, m.id_member,
				m.icon, m.poster_ip, m.body, m.smileys_enabled, m.modified_time, m.modified_name,
				first_m.id_msg AS first_msg, first_m.subject AS first_subject, first_m.icon AS first_icon, first_m.poster_time AS first_poster_time,
				first_mem.id_member AS first_member_id, IFNULL(first_mem.real_name, first_m.poster_name) AS first_member_name,
				last_m.id_msg AS last_msg, last_m.poster_time AS last_poster_time, last_mem.id_member AS last_member_id,
				IFNULL(last_mem.real_name, last_m.poster_name) AS last_member_name, last_m.icon AS last_icon, last_m.subject AS last_subject,
				t.id_topic, t.is_sticky, t.locked, t.id_poll, t.num_replies, t.num_views,
				b.id_board, b.name AS board_name, c.id_cat, c.name AS cat_name
			FROM {db_prefix}messages AS m
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic)
				INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
				INNER JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
				INNER JOIN {db_prefix}messages AS first_m ON (first_m.id_msg = t.id_first_msg)
				INNER JOIN {db_prefix}messages AS last_m ON (last_m.id_msg = t.id_last_msg)
				LEFT JOIN {db_prefix}members AS first_mem ON (first_mem.id_member = first_m.id_member)
				LEFT JOIN {db_prefix}members AS last_mem ON (last_mem.id_member = first_m.id_member)
			WHERE m.id_msg IN ({array_int:message_list})' . ($modSettings['postmod_active'] ? '
				AND m.approved = {int:is_approved}' : '') . '
			ORDER BY FIND_IN_SET(m.id_msg, {string:message_list_in_set})
			LIMIT {int:limit}', array('message_list' => $msg_list, 'is_approved' => 1, 'message_list_in_set' => implode(',', $msg_list), 'limit' => count($context['topics'])));
        // If there are no results that means the things in the cache got deleted, so pretend we have no topics anymore.
        if ($smcFunc['db_num_rows']($messages_request) == 0) {
            $context['topics'] = array();
        }
        // If we want to know who participated in what then load this now.
        if (!empty($modSettings['enableParticipation']) && !$user_info['is_guest']) {
            $result = $smcFunc['db_query']('', '
				SELECT id_topic
				FROM {db_prefix}messages
				WHERE id_topic IN ({array_int:topic_list})
					AND id_member = {int:current_member}
				GROUP BY id_topic
				LIMIT ' . count($participants), array('current_member' => $user_info['id'], 'topic_list' => array_keys($participants)));
            while ($row = $smcFunc['db_fetch_assoc']($result)) {
                $participants[$row['id_topic']] = true;
            }
            $smcFunc['db_free_result']($result);
        }
    }
    // 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'], $num_results, $modSettings['search_results_per_page'], false);
    // 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'] : $user_info['id']), null, 90);
    }
    $context['key_words'] =& $searchArray;
    // Setup the default topic icons... for checking they exist and the like!
    $stable_icons = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'moved', 'recycled', 'wireless', 'clip');
    $context['icon_sources'] = array();
    foreach ($stable_icons as $icon) {
        $context['icon_sources'][$icon] = 'images_url';
    }
    $context['sub_template'] = 'results';
    $context['page_title'] = $txt['search_results'];
    $context['get_topics'] = 'prepareSearchContext';
    $context['can_send_pm'] = allowedTo('pm_send');
    $context['jump_to'] = array('label' => addslashes(un_htmlspecialchars($txt['jump_to'])), 'board_name' => addslashes(un_htmlspecialchars($txt['select_destination'])));
}
 /**
  * Send the emails.
  *
  * - Sends off emails to all the moderators.
  * - Sends to administrators and global moderators. (1 and 2)
  * - Called by action_reporttm(), and thus has the same permission and setting requirements as it does.
  * - Accessed through ?action=reporttm when posting.
  */
 public function action_reporttm2()
 {
     global $txt, $scripturl, $topic, $board, $user_info, $modSettings, $language, $context;
     // You must have the proper permissions!
     isAllowedTo('report_any');
     // Make sure they aren't spamming.
     spamProtection('reporttm');
     require_once SUBSDIR . '/Mail.subs.php';
     // No errors, yet.
     $report_errors = Error_Context::context('report', 1);
     // Check their session.
     if (checkSession('post', '', false) != '') {
         $report_errors->addError('session_timeout');
     }
     // Make sure we have a comment and it's clean.
     if (!isset($_POST['comment']) || Util::htmltrim($_POST['comment']) === '') {
         $report_errors->addError('no_comment');
     }
     $poster_comment = strtr(Util::htmlspecialchars($_POST['comment']), array("\r" => '', "\t" => ''));
     if (Util::strlen($poster_comment) > 254) {
         $report_errors->addError('post_too_long');
     }
     // Guests need to provide their address!
     if ($user_info['is_guest']) {
         require_once SUBSDIR . '/DataValidator.class.php';
         if (!Data_Validator::is_valid($_POST, array('email' => 'valid_email'), array('email' => 'trim'))) {
             empty($_POST['email']) ? $report_errors->addError('no_email') : $report_errors->addError('bad_email');
         }
         isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
         $user_info['email'] = htmlspecialchars($_POST['email'], ENT_COMPAT, 'UTF-8');
     }
     // Could they get the right verification code?
     if ($user_info['is_guest'] && !empty($modSettings['guests_report_require_captcha'])) {
         require_once SUBSDIR . '/VerificationControls.class.php';
         $verificationOptions = array('id' => 'report');
         $context['require_verification'] = create_control_verification($verificationOptions, true);
         if (is_array($context['require_verification'])) {
             foreach ($context['require_verification'] as $error) {
                 $report_errors->addError($error, 0);
             }
         }
     }
     // Any errors?
     if ($report_errors->hasErrors()) {
         return $this->action_reporttm();
     }
     // Get the basic topic information, and make sure they can see it.
     $msg_id = (int) $_POST['msg'];
     $message = posterDetails($msg_id, $topic);
     if (empty($message)) {
         fatal_lang_error('no_board', false);
     }
     $poster_name = un_htmlspecialchars($message['real_name']) . ($message['real_name'] != $message['poster_name'] ? ' (' . $message['poster_name'] . ')' : '');
     $reporterName = un_htmlspecialchars($user_info['name']) . ($user_info['name'] != $user_info['username'] && $user_info['username'] != '' ? ' (' . $user_info['username'] . ')' : '');
     $subject = un_htmlspecialchars($message['subject']);
     // Get a list of members with the moderate_board permission.
     require_once SUBSDIR . '/Members.subs.php';
     $moderators = membersAllowedTo('moderate_board', $board);
     $result = getBasicMemberData($moderators, array('preferences' => true, 'sort' => 'lngfile'));
     $mod_to_notify = array();
     foreach ($result as $row) {
         if ($row['notify_types'] != 4) {
             $mod_to_notify[] = $row;
         }
     }
     // Check that moderators do exist!
     if (empty($mod_to_notify)) {
         fatal_lang_error('no_mods', false);
     }
     // If we get here, I believe we should make a record of this, for historical significance, yabber.
     if (empty($modSettings['disable_log_report'])) {
         require_once SUBSDIR . '/Messages.subs.php';
         $id_report = recordReport($message, $poster_comment);
         // If we're just going to ignore these, then who gives a monkeys...
         if ($id_report === false) {
             redirectexit('topic=' . $topic . '.msg' . $msg_id . '#msg' . $msg_id);
         }
     }
     // Find out who the real moderators are - for mod preferences.
     require_once SUBSDIR . '/Boards.subs.php';
     $real_mods = getBoardModerators($board, true);
     // Send every moderator an email.
     foreach ($mod_to_notify as $row) {
         // Maybe they don't want to know?!
         if (!empty($row['mod_prefs'])) {
             list(, , $pref_binary) = explode('|', $row['mod_prefs']);
             if (!($pref_binary & 1) && (!($pref_binary & 2) || !in_array($row['id_member'], $real_mods))) {
                 continue;
             }
         }
         $replacements = array('TOPICSUBJECT' => $subject, 'POSTERNAME' => $poster_name, 'REPORTERNAME' => $reporterName, 'TOPICLINK' => $scripturl . '?topic=' . $topic . '.msg' . $msg_id . '#msg' . $msg_id, 'REPORTLINK' => !empty($id_report) ? $scripturl . '?action=moderate;area=reports;report=' . $id_report : '', 'COMMENT' => $_POST['comment']);
         $emaildata = loadEmailTemplate('report_to_moderator', $replacements, empty($row['lngfile']) || empty($modSettings['userLanguage']) ? $language : $row['lngfile']);
         // Send it to the moderator.
         sendmail($row['email_address'], $emaildata['subject'], $emaildata['body'], $user_info['email'], null, false, 2);
     }
     // Keep track of when the mod reports get updated, that way we know when we need to look again.
     updateSettings(array('last_mod_report_action' => time()));
     // Back to the post we reported!
     redirectexit('reportsent;topic=' . $topic . '.msg' . $msg_id . '#msg' . $msg_id);
 }
function Register2($verifiedOpenID = false)
{
    global $scripturl, $txt, $modSettings, $context, $sourcedir;
    global $user_info, $options, $settings, $smcFunc;
    // Start collecting together any errors.
    $reg_errors = array();
    // Did we save some open ID fields?
    if ($verifiedOpenID && !empty($context['openid_save_fields'])) {
        foreach ($context['openid_save_fields'] as $id => $value) {
            $_POST[$id] = $value;
        }
    }
    // You can't register if it's disabled.
    if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 3) {
        fatal_lang_error('registration_disabled', false);
    }
    // Things we don't do for people who have already confirmed their OpenID allegances via register.
    if (!$verifiedOpenID) {
        // Well, if you don't agree, you can't register.
        if (!empty($modSettings['requireAgreement']) && empty($_SESSION['registration_agreed'])) {
            redirectexit();
        }
        // Make sure they came from *somewhere*, have a session.
        if (!isset($_SESSION['old_url'])) {
            redirectexit('action=register');
        }
        // Are they under age, and under age users are banned?
        if (!empty($modSettings['coppaAge']) && empty($modSettings['coppaType']) && empty($_SESSION['skip_coppa'])) {
            // !!! This should be put in Errors, imho.
            loadLanguage('Login');
            fatal_lang_error('under_age_registration_prohibited', false, array($modSettings['coppaAge']));
        }
        // Check whether the visual verification code was entered correctly.
        if (!empty($modSettings['reg_verification'])) {
            require_once $sourcedir . '/Subs-Editor.php';
            $verificationOptions = array('id' => 'register');
            $context['visual_verification'] = create_control_verification($verificationOptions, true);
            if (is_array($context['visual_verification'])) {
                loadLanguage('Errors');
                foreach ($context['visual_verification'] as $error) {
                    $reg_errors[] = $txt['error_' . $error];
                }
            }
        }
    }
    foreach ($_POST as $key => $value) {
        if (!is_array($_POST[$key])) {
            $_POST[$key] = htmltrim__recursive(str_replace(array("\n", "\r"), '', $_POST[$key]));
        }
    }
    // Collect all extra registration fields someone might have filled in.
    $possible_strings = array('website_url', 'website_title', 'aim', 'yim', 'skype', 'gtalk', 'location', 'birthdate', 'time_format', 'buddy_list', 'pm_ignore_list', 'smiley_set', 'signature', 'personal_text', 'avatar', 'lngfile', 'secret_question', 'secret_answer');
    $possible_ints = array('pm_email_notify', 'notify_types', 'icq', 'gender', 'id_theme');
    $possible_floats = array('time_offset');
    $possible_bools = array('notify_announcements', 'notify_regularity', 'notify_send_body', 'hide_email', 'show_online');
    if (isset($_POST['secret_answer']) && $_POST['secret_answer'] != '') {
        $_POST['secret_answer'] = md5($_POST['secret_answer']);
    }
    // Needed for isReservedName() and registerMember().
    require_once $sourcedir . '/Subs-Members.php';
    // Validation... even if we're not a mall.
    if (isset($_POST['real_name']) && (!empty($modSettings['allow_editDisplayName']) || allowedTo('moderate_forum'))) {
        $_POST['real_name'] = trim(preg_replace('~[\\t\\n\\r \\x0B\\0' . ($context['utf8'] ? $context['server']['complex_preg_chars'] ? '\\x{A0}\\x{AD}\\x{2000}-\\x{200F}\\x{201F}\\x{202F}\\x{3000}\\x{FEFF}' : " ­ -‏‟ ‟ " : '\\x00-\\x08\\x0B\\x0C\\x0E-\\x19\\xA0') . ']+~' . ($context['utf8'] ? 'u' : ''), ' ', $_POST['real_name']));
        if (trim($_POST['real_name']) != '' && !isReservedName($_POST['real_name']) && $smcFunc['strlen']($_POST['real_name']) < 60) {
            $possible_strings[] = 'real_name';
        }
    }
    if (isset($_POST['msn']) && preg_match('~^[0-9A-Za-z=_+\\-/][0-9A-Za-z=_\'+\\-/\\.]*@[\\w\\-]+(\\.[\\w\\-]+)*(\\.[\\w]{2,6})$~', $_POST['msn']) != 0) {
        $profile_strings[] = 'msn';
    }
    // Handle a string as a birthdate...
    if (isset($_POST['birthdate']) && $_POST['birthdate'] != '') {
        $_POST['birthdate'] = strftime('%Y-%m-%d', strtotime($_POST['birthdate']));
    } elseif (!empty($_POST['bday1']) && !empty($_POST['bday2'])) {
        $_POST['birthdate'] = sprintf('%04d-%02d-%02d', empty($_POST['bday3']) ? 0 : (int) $_POST['bday3'], (int) $_POST['bday1'], (int) $_POST['bday2']);
    }
    // By default assume email is hidden, only show it if we tell it to.
    $_POST['hide_email'] = !empty($_POST['allow_email']) ? 0 : 1;
    // Validate the passed language file.
    if (isset($_POST['lngfile']) && !empty($modSettings['userLanguage'])) {
        // Do we have any languages?
        if (empty($context['languages'])) {
            getLanguages();
        }
        // Did we find it?
        if (isset($context['languages'][$_POST['lngfile']])) {
            $_SESSION['language'] = $_POST['lngfile'];
        } else {
            unset($_POST['lngfile']);
        }
    } else {
        unset($_POST['lngfile']);
    }
    // Some of these fields we may not want.
    if (!empty($modSettings['registration_fields'])) {
        // But we might want some of them if the admin asks for them.
        $standard_fields = array('icq', 'msn', 'aim', 'yim', 'location', 'gender');
        $reg_fields = explode(',', $modSettings['registration_fields']);
        $exclude_fields = array_diff($standard_fields, $reg_fields);
        // Website is a little different
        if (!in_array('website', $reg_fields)) {
            $exclude_fields = array_merge($exclude_fields, array('website_url', 'website_title'));
        }
        // We used to accept signature on registration but it's being abused by spammers these days, so no more.
        $exclude_fields[] = 'signature';
    } else {
        $exclude_fields = array('signature', 'icq', 'msn', 'aim', 'yim', 'location', 'gender', 'website_url', 'website_title');
    }
    $possible_strings = array_diff($possible_strings, $exclude_fields);
    $possible_ints = array_diff($possible_ints, $exclude_fields);
    $possible_floats = array_diff($possible_floats, $exclude_fields);
    $possible_bools = array_diff($possible_bools, $exclude_fields);
    // Set the options needed for registration.
    $regOptions = array('interface' => 'guest', 'username' => !empty($_POST['user']) ? $_POST['user'] : '', 'email' => !empty($_POST['email']) ? $_POST['email'] : '', 'password' => !empty($_POST['passwrd1']) ? $_POST['passwrd1'] : '', 'password_check' => !empty($_POST['passwrd2']) ? $_POST['passwrd2'] : '', 'openid' => !empty($_POST['openid_identifier']) ? $_POST['openid_identifier'] : '', 'auth_method' => !empty($_POST['authenticate']) ? $_POST['authenticate'] : '', 'check_reserved_name' => true, 'check_password_strength' => true, 'check_email_ban' => true, 'send_welcome_email' => !empty($modSettings['send_welcomeEmail']), 'require' => !empty($modSettings['coppaAge']) && !$verifiedOpenID && empty($_SESSION['skip_coppa']) ? 'coppa' : (empty($modSettings['registration_method']) ? 'nothing' : ($modSettings['registration_method'] == 1 ? 'activation' : 'approval')), 'extra_register_vars' => array(), 'theme_vars' => array());
    // Include the additional options that might have been filled in.
    foreach ($possible_strings as $var) {
        if (isset($_POST[$var])) {
            $regOptions['extra_register_vars'][$var] = $smcFunc['htmlspecialchars']($_POST[$var], ENT_QUOTES);
        }
    }
    foreach ($possible_ints as $var) {
        if (isset($_POST[$var])) {
            $regOptions['extra_register_vars'][$var] = (int) $_POST[$var];
        }
    }
    foreach ($possible_floats as $var) {
        if (isset($_POST[$var])) {
            $regOptions['extra_register_vars'][$var] = (double) $_POST[$var];
        }
    }
    foreach ($possible_bools as $var) {
        if (isset($_POST[$var])) {
            $regOptions['extra_register_vars'][$var] = empty($_POST[$var]) ? 0 : 1;
        }
    }
    // Registration options are always default options...
    if (isset($_POST['default_options'])) {
        $_POST['options'] = isset($_POST['options']) ? $_POST['options'] + $_POST['default_options'] : $_POST['default_options'];
    }
    $regOptions['theme_vars'] = isset($_POST['options']) && is_array($_POST['options']) ? $_POST['options'] : array();
    // Make sure they are clean, dammit!
    $regOptions['theme_vars'] = htmlspecialchars__recursive($regOptions['theme_vars']);
    // If Quick Reply hasn't been set then set it to be shown but collapsed.
    if (!isset($regOptions['theme_vars']['display_quick_reply'])) {
        $regOptions['theme_vars']['display_quick_reply'] = 1;
    }
    // Check whether we have fields that simply MUST be displayed?
    $request = $smcFunc['db_query']('', '
		SELECT col_name, field_name, field_type, field_length, mask, show_reg
		FROM {db_prefix}custom_fields
		WHERE active = {int:is_active}', array('is_active' => 1));
    $custom_field_errors = array();
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        // Don't allow overriding of the theme variables.
        if (isset($regOptions['theme_vars'][$row['col_name']])) {
            unset($regOptions['theme_vars'][$row['col_name']]);
        }
        // Not actually showing it then?
        if (!$row['show_reg']) {
            continue;
        }
        // Prepare the value!
        $value = isset($_POST['customfield'][$row['col_name']]) ? trim($_POST['customfield'][$row['col_name']]) : '';
        // We only care for text fields as the others are valid to be empty.
        if (!in_array($row['field_type'], array('check', 'select', 'radio'))) {
            // Is it too long?
            if ($row['field_length'] && $row['field_length'] < $smcFunc['strlen']($value)) {
                $custom_field_errors[] = array('custom_field_too_long', array($row['field_name'], $row['field_length']));
            }
            // Any masks to apply?
            if ($row['field_type'] == 'text' && !empty($row['mask']) && $row['mask'] != 'none') {
                //!!! We never error on this - just ignore it at the moment...
                if ($row['mask'] == 'email' && (preg_match('~^[0-9A-Za-z=_+\\-/][0-9A-Za-z=_\'+\\-/\\.]*@[\\w\\-]+(\\.[\\w\\-]+)*(\\.[\\w]{2,6})$~', $value) === 0 || strlen($value) > 255)) {
                    $custom_field_errors[] = array('custom_field_invalid_email', array($row['field_name']));
                } elseif ($row['mask'] == 'number' && preg_match('~[^\\d]~', $value)) {
                    $custom_field_errors[] = array('custom_field_not_number', array($row['field_name']));
                } elseif (substr($row['mask'], 0, 5) == 'regex' && trim($value) != '' && preg_match(substr($row['mask'], 5), $value) === 0) {
                    $custom_field_errors[] = array('custom_field_inproper_format', array($row['field_name']));
                }
            }
        }
        // xxx if we are editing our minecraft name, make sure there are no duplicates
        if (($row['col_name'] == "cust_minecra" || $row['col_name'] == "cust_rscnam") && $value != '') {
            $already_taken_memID = -1;
            $already_taken_memName = 'This user';
            // first check the custom names
            $mc_request = $smcFunc['db_query']('', '
						SELECT `id_member`
						FROM `{db_prefix}themes`
						WHERE `variable` = {string:col_name}
							AND `value` = {string:value}', array('col_name' => $row['col_name'], 'value' => strtolower($value)));
            if ($mc_row = $smcFunc['db_fetch_assoc']($mc_request)) {
                $already_taken_memID = $mc_row['id_member'];
            }
            $smcFunc['db_free_result']($mc_request);
            // if custom name is not taken, compare it to account names, or just grab name
            $mc_request = $smcFunc['db_query']('', '
						SELECT `id_member`, `real_name`
						FROM `{db_prefix}members`
						WHERE id_member = {int:already_taken_memID} OR 
								(
									(
										`real_name` = {string:value}
										OR `member_name` = {string:value}
									)
								)', array('already_taken_memID' => $already_taken_memID, 'value' => strtolower($value)));
            if ($mc_row = $smcFunc['db_fetch_assoc']($mc_request)) {
                $already_taken_memID = $mc_row['id_member'];
                $already_taken_memName = $mc_row['real_name'];
            }
            $smcFunc['db_free_result']($mc_request);
            if ($already_taken_memID != -1) {
                // then someone already is using this name
                global $boardurl;
                $what_name = $row['col_name'] == "cust_minecra" ? 'Minecraft' : 'RSC';
                die('<html>Error: <a href="' . $boardurl . '/index.php?action=profile;u=' . $already_taken_memID . "\">{$already_taken_memName}</a> has already registered this {$what_name} name!</html>");
            }
        }
        if ($row['col_name'] == "cust_moparcr" && $value != '' && strlen($value) != 40) {
            if (strlen($value) > 30) {
                die("<html>Error: Maximum length for MoparCraft server password is 30 characters.</html>");
            }
            if ($value == $regOptions['password']) {
                die("<html>Error: You can't set your MoparCraft server password to be the same as your forum password, if you want to use your forum password, leave this blank.</html>");
            }
            $value = sha1(strtolower($regOptions['username']) . htmlspecialchars_decode($value));
            $_POST['customfield'][$row['col_name']] = $value;
        }
        // xxx end if we are editing our minecraft name, make sure there are no duplicates
        // Is this required but not there?
        if (trim($value) == '' && $row['show_reg'] > 1) {
            $custom_field_errors[] = array('custom_field_empty', array($row['field_name']));
        }
    }
    $smcFunc['db_free_result']($request);
    // Process any errors.
    if (!empty($custom_field_errors)) {
        loadLanguage('Errors');
        foreach ($custom_field_errors as $error) {
            $reg_errors[] = vsprintf($txt['error_' . $error[0]], $error[1]);
        }
    }
    // Lets check for other errors before trying to register the member.
    if (!empty($reg_errors)) {
        $_REQUEST['step'] = 2;
        return Register($reg_errors);
    }
    // If they're wanting to use OpenID we need to validate them first.
    if (empty($_SESSION['openid']['verified']) && !empty($_POST['authenticate']) && $_POST['authenticate'] == 'openid') {
        // What do we need to save?
        $save_variables = array();
        foreach ($_POST as $k => $v) {
            if (!in_array($k, array('sc', 'sesc', $context['session_var'], 'passwrd1', 'passwrd2', 'regSubmit'))) {
                $save_variables[$k] = $v;
            }
        }
        require_once $sourcedir . '/Subs-OpenID.php';
        smf_openID_validate($_POST['openid_identifier'], false, $save_variables);
    } elseif ($verifiedOpenID || !empty($_POST['openid_identifier']) && $_POST['authenticate'] == 'openid') {
        $regOptions['username'] = !empty($_POST['user']) && trim($_POST['user']) != '' ? $_POST['user'] : $_SESSION['openid']['nickname'];
        $regOptions['email'] = !empty($_POST['email']) && trim($_POST['email']) != '' ? $_POST['email'] : $_SESSION['openid']['email'];
        $regOptions['auth_method'] = 'openid';
        $regOptions['openid'] = !empty($_POST['openid_identifier']) ? $_POST['openid_identifier'] : $_SESSION['openid']['openid_uri'];
    }
    $memberID = registerMember($regOptions, true);
    // What there actually an error of some kind dear boy?
    if (is_array($memberID)) {
        $reg_errors = array_merge($reg_errors, $memberID);
        $_REQUEST['step'] = 2;
        return Register($reg_errors);
    }
    // Do our spam protection now.
    spamProtection('register');
    // We'll do custom fields after as then we get to use the helper function!
    if (!empty($_POST['customfield'])) {
        require_once $sourcedir . '/Profile.php';
        require_once $sourcedir . '/Profile-Modify.php';
        makeCustomFieldChanges($memberID, 'register');
    }
    // If COPPA has been selected then things get complicated, setup the template.
    if (!empty($modSettings['coppaAge']) && empty($_SESSION['skip_coppa'])) {
        redirectexit('action=coppa;member=' . $memberID);
    } elseif (!empty($modSettings['registration_method'])) {
        loadTemplate('Register');
        $context += array('page_title' => $txt['register'], 'title' => $txt['registration_successful'], 'sub_template' => 'after', 'description' => $modSettings['registration_method'] == 2 ? $txt['approval_after_registration'] : $txt['activate_after_registration']);
    } else {
        call_integration_hook('integrate_activate', array($row['member_name']));
        setLoginCookie(60 * $modSettings['cookieTime'], $memberID, sha1(sha1(strtolower($regOptions['username']) . $regOptions['password']) . $regOptions['register_vars']['password_salt']));
        redirectexit('action=login2;sa=check;member=' . $memberID, $context['server']['needs_login_fix']);
    }
}
Beispiel #11
0
function Display()
{
    global $scripturl, $txt, $modSettings, $context, $settings, $memberContext, $output;
    global $options, $sourcedir, $user_info, $user_profile, $board_info, $topic, $board;
    global $attachments, $messages_request, $topicinfo, $language;
    $context['response_prefixlen'] = strlen($txt['response_prefix']);
    $context['need_synhlt'] = true;
    $context['is_display_std'] = true;
    $context['pcache_update_counter'] = !empty($modSettings['use_post_cache']) ? 0 : PCACHE_UPDATE_PER_VIEW + 1;
    $context['time_cutoff_ref'] = time();
    $context['template_hooks']['display'] = array('header' => '', 'extend_topicheader' => '', 'above_posts' => '', 'below_posts' => '', 'footer' => '');
    //EoS_Smarty::getConfigInstance()->registerHookTemplate('postbit_below', 'overrides/foo');
    if (!empty($modSettings['karmaMode'])) {
        require_once $sourcedir . '/lib/Subs-Ratings.php';
    } else {
        $context['can_see_like'] = $context['can_give_like'] = false;
    }
    // What are you gonna display if these are empty?!
    if (empty($topic)) {
        fatal_lang_error('no_board', false);
    }
    // Not only does a prefetch make things slower for the server, but it makes it impossible to know if they read it.
    if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') {
        ob_end_clean();
        header('HTTP/1.1 403 Prefetch Forbidden');
        die;
    }
    // How much are we sticking on each page?
    $context['messages_per_page'] = commonAPI::getMessagesPerPage();
    $context['page_number'] = isset($_REQUEST['start']) ? $_REQUEST['start'] / $context['messages_per_page'] : 0;
    // Let's do some work on what to search index.
    //$context['multiquote_cookiename'] = 'mq_' . $context['current_topic'];
    $context['multiquote_posts'] = array();
    if (isset($_COOKIE[$context['multiquote_cookiename']]) && strlen($_COOKIE[$context['multiquote_cookiename']]) > 1) {
        $context['multiquote_posts'] = explode(',', $_COOKIE[$context['multiquote_cookiename']]);
    }
    $context['multiquote_posts_count'] = count($context['multiquote_posts']);
    if (count($_GET) > 2) {
        foreach ($_GET as $k => $v) {
            if (!in_array($k, array('topic', 'board', 'start', session_name()))) {
                $context['robot_no_index'] = true;
            }
        }
    }
    if (!empty($_REQUEST['start']) && (!is_numeric($_REQUEST['start']) || $_REQUEST['start'] % $context['messages_per_page'] != 0)) {
        $context['robot_no_index'] = true;
    }
    // Find the previous or next topic.  Make a fuss if there are no more.
    if (isset($_REQUEST['prev_next']) && ($_REQUEST['prev_next'] == 'prev' || $_REQUEST['prev_next'] == 'next')) {
        // No use in calculating the next topic if there's only one.
        if ($board_info['num_topics'] > 1) {
            // Just prepare some variables that are used in the query.
            $gt_lt = $_REQUEST['prev_next'] == 'prev' ? '>' : '<';
            $order = $_REQUEST['prev_next'] == 'prev' ? '' : ' DESC';
            $request = smf_db_query('
				SELECT t2.id_topic
				FROM {db_prefix}topics AS t
					INNER JOIN {db_prefix}topics AS t2 ON (' . (empty($modSettings['enableStickyTopics']) ? '
					t2.id_last_msg ' . $gt_lt . ' t.id_last_msg' : '
					(t2.id_last_msg ' . $gt_lt . ' t.id_last_msg AND t2.is_sticky ' . $gt_lt . '= t.is_sticky) OR t2.is_sticky ' . $gt_lt . ' t.is_sticky') . ')
				WHERE t.id_topic = {int:current_topic}
					AND t2.id_board = {int:current_board}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
					AND (t2.approved = {int:is_approved} OR (t2.id_member_started != {int:id_member_started} AND t2.id_member_started = {int:current_member}))') . '
				ORDER BY' . (empty($modSettings['enableStickyTopics']) ? '' : ' t2.is_sticky' . $order . ',') . ' t2.id_last_msg' . $order . '
				LIMIT 1', array('current_board' => $board, 'current_member' => $user_info['id'], 'current_topic' => $topic, 'is_approved' => 1, 'id_member_started' => 0));
            // 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 = smf_db_query('
					SELECT id_topic
					FROM {db_prefix}topics
					WHERE id_board = {int:current_board}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
						AND (approved = {int:is_approved} OR (id_member_started != {int:id_member_started} AND id_member_started = {int:current_member}))') . '
					ORDER BY' . (empty($modSettings['enableStickyTopics']) ? '' : ' is_sticky' . $order . ',') . ' id_last_msg' . $order . '
					LIMIT 1', array('current_board' => $board, 'current_member' => $user_info['id'], 'is_approved' => 1, 'id_member_started' => 0));
            }
            // 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';
    }
    // Add 1 to the number of views of this topic.
    if (empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic) {
        smf_db_query('
			UPDATE {db_prefix}topics
			SET num_views = num_views + 1
			WHERE id_topic = {int:current_topic}', array('current_topic' => $topic));
        $_SESSION['last_read_topic'] = $topic;
    }
    if ($modSettings['tags_active']) {
        $dbresult = smf_db_query('
		   SELECT t.tag,l.ID,t.ID_TAG FROM {db_prefix}tags_log as l, {db_prefix}tags as t
			WHERE t.ID_TAG = l.ID_TAG && l.ID_TOPIC = {int:topic}', array('topic' => $topic));
        $context['topic_tags'] = array();
        while ($row = mysql_fetch_assoc($dbresult)) {
            $context['topic_tags'][] = array('ID' => $row['ID'], 'ID_TAG' => $row['ID_TAG'], 'tag' => $row['tag']);
        }
        mysql_free_result($dbresult);
        $context['tags_active'] = true;
    } else {
        $context['topic_tags'] = $context['tags_active'] = 0;
    }
    // Get all the important topic info.
    $request = smf_db_query('SELECT
			t.num_replies, t.num_views, t.locked, ms.poster_name, ms.subject, ms.poster_email, ms.poster_time AS first_post_time, t.is_sticky, t.id_poll,
			t.id_member_started, t.id_first_msg, t.id_last_msg, t.approved, t.unapproved_posts, t.id_layout, 
			' . ($user_info['is_guest'] ? 't.id_last_msg + 1' : 'IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1') . ' AS new_from
			' . (!empty($modSettings['recycle_board']) && $modSettings['recycle_board'] == $board ? ', id_previous_board, id_previous_topic' : '') . ',
			p.name AS prefix_name, ms1.poster_time AS last_post_time, ms1.modified_time AS last_modified_time, IFNULL(b.automerge, 0) AS automerge
		FROM {db_prefix}topics AS t
			INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
			INNER JOIN {db_prefix}messages AS ms1 ON (ms1.id_msg = t.id_last_msg)
			INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)' . ($user_info['is_guest'] ? '' : '
			LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = {int:current_topic} AND lt.id_member = {int:current_member})
			LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})') . '
			LEFT JOIN {db_prefix}prefixes as p ON p.id_prefix = t.id_prefix 
		WHERE t.id_topic = {int:current_topic}
		LIMIT 1', array('current_member' => $user_info['id'], 'current_topic' => $topic, 'current_board' => $board));
    if (mysql_num_rows($request) == 0) {
        fatal_lang_error('not_a_topic', false);
    }
    // Added by Related Topics
    if (isset($modSettings['have_related_topics']) && $modSettings['have_related_topics'] && !empty($modSettings['relatedTopicsEnabled'])) {
        require_once $sourcedir . '/lib/Subs-Related.php';
        loadRelated($topic);
    }
    $topicinfo = mysql_fetch_assoc($request);
    mysql_free_result($request);
    $context['topic_banned_members'] = array();
    $request = smf_db_query('SELECT id_member FROM {db_prefix}topicbans WHERE id_topic = {int:topic}', array('topic' => $topic));
    if (mysql_num_rows($request) != 0) {
        while ($row = mysql_fetch_row($request)) {
            $context['topic_banned_members'][] = $row[0];
        }
    }
    mysql_free_result($request);
    $context['topic_banned_members_count'] = count($context['topic_banned_members']);
    $context['topic_last_modified'] = max($topicinfo['last_post_time'], $topicinfo['last_modified_time']);
    // todo: considering - make post cutoff time for the cache depend on the modification time of the topic's last post
    $context['real_num_replies'] = $context['num_replies'] = $topicinfo['num_replies'];
    $context['topic_first_message'] = $topicinfo['id_first_msg'];
    $context['topic_last_message'] = $topicinfo['id_last_msg'];
    $context['first_subject'] = $topicinfo['subject'];
    $context['prefix'] = !empty($topicinfo['prefix_name']) ? html_entity_decode($topicinfo['prefix_name']) . '&nbsp;' : '';
    $context['automerge'] = $topicinfo['automerge'] > 0;
    // Add up unapproved replies to get real number of replies...
    if ($modSettings['postmod_active'] && allowedTo('approve_posts')) {
        $context['real_num_replies'] += $topicinfo['unapproved_posts'] - ($topicinfo['approved'] ? 0 : 1);
    }
    // If this topic has unapproved posts, we need to work out how many posts the user can see, for page indexing.
    if ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !$user_info['is_guest'] && !allowedTo('approve_posts')) {
        $request = smf_db_query('
			SELECT COUNT(id_member) AS my_unapproved_posts
			FROM {db_prefix}messages
			WHERE id_topic = {int:current_topic}
				AND id_member = {int:current_member}
				AND approved = 0', array('current_topic' => $topic, 'current_member' => $user_info['id']));
        list($myUnapprovedPosts) = mysql_fetch_row($request);
        mysql_free_result($request);
        $context['total_visible_posts'] = $context['num_replies'] + $myUnapprovedPosts + ($topicinfo['approved'] ? 1 : 0);
    } else {
        $context['total_visible_posts'] = $context['num_replies'] + $topicinfo['unapproved_posts'] + ($topicinfo['approved'] ? 1 : 0);
    }
    // When was the last time this topic was replied to?  Should we warn them about it?
    /* redundant query? last_post_time is already in $topicinfo[]
    	$request = smf_db_query( '
    		SELECT poster_time
    		FROM {db_prefix}messages
    		WHERE id_msg = {int:id_last_msg}
    		LIMIT 1',
    		array(
    			'id_last_msg' => $topicinfo['id_last_msg'],
    		)
    	);
    
    	list ($lastPostTime) = mysql_fetch_row($request);
    	mysql_free_result($request);
    	*/
    $lastPostTime = $topicinfo['last_post_time'];
    $context['oldTopicError'] = !empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky);
    // The start isn't a number; it's information about what to do, where to go.
    if (!is_numeric($_REQUEST['start'])) {
        // Redirect to the page and post with new messages, originally by Omar Bazavilvazo.
        if ($_REQUEST['start'] == 'new') {
            // Guests automatically go to the last post.
            if ($user_info['is_guest']) {
                $context['start_from'] = $context['total_visible_posts'] - 1;
                $_REQUEST['start'] = 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 = smf_db_query('
					SELECT IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from
					FROM {db_prefix}topics AS t
						LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = {int:current_topic} AND lt.id_member = {int:current_member})
						LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})
					WHERE t.id_topic = {int:current_topic}
					LIMIT 1', array('current_board' => $board, 'current_member' => $user_info['id'], 'current_topic' => $topic));
                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 = smf_db_query('
					SELECT COUNT(*)
					FROM {db_prefix}messages
					WHERE poster_time < {int:timestamp}
						AND id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts') ? '
						AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')' : ''), array('current_topic' => $topic, 'current_member' => $user_info['id'], 'is_approved' => 1, 'timestamp' => $timestamp));
                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'] : $context['total_visible_posts'] - $context['start_from'] - 1;
            }
        } elseif (substr($_REQUEST['start'], 0, 3) == 'msg') {
            $virtual_msg = (int) substr($_REQUEST['start'], 3);
            if (!$topicinfo['unapproved_posts'] && $virtual_msg >= $topicinfo['id_last_msg']) {
                $context['start_from'] = $context['total_visible_posts'] - 1;
            } elseif (!$topicinfo['unapproved_posts'] && $virtual_msg <= $topicinfo['id_first_msg']) {
                $context['start_from'] = 0;
            } else {
                // Find the start value for that message......
                $request = smf_db_query('
					SELECT COUNT(*)
					FROM {db_prefix}messages
					WHERE id_msg < {int:virtual_msg}
						AND id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts') ? '
						AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')' : ''), array('current_member' => $user_info['id'], 'current_topic' => $topic, 'virtual_msg' => $virtual_msg, 'is_approved' => 1, 'no_member' => 0));
                list($context['start_from']) = mysql_fetch_row($request);
                mysql_free_result($request);
            }
            // We need to reverse the start as well in this case.
            if (isset($_REQUEST['perma'])) {
                $_REQUEST['start'] = $virtual_msg;
            } else {
                $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $context['total_visible_posts'] - $context['start_from'] - 1;
            }
        }
    }
    // 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>' : '';
    // Do we need to show the visual verification image?
    $context['require_verification'] = !$user_info['is_mod'] && !$user_info['is_admin'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || $user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1);
    if ($context['require_verification']) {
        require_once $sourcedir . '/lib/Subs-Editor.php';
        $verificationOptions = array('id' => 'post', 'skip_template' => true);
        $context['require_verification'] = create_control_verification($verificationOptions);
        $context['visual_verification_id'] = $verificationOptions['id'];
    }
    // Are we showing signatures - or disabled fields?
    $context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1;
    $context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
    // Censor the title...
    censorText($topicinfo['subject']);
    $context['page_title'] = $topicinfo['subject'] . ((int) $context['page_number'] > 0 ? ' - ' . $txt['page'] . ' ' . ($context['page_number'] + 1) : '');
    // Is this topic sticky, or can it even be?
    $topicinfo['is_sticky'] = empty($modSettings['enableStickyTopics']) ? '0' : $topicinfo['is_sticky'];
    // Default this topic to not marked for notifications... of course...
    $context['is_marked_notify'] = false;
    // Did we report a post to a moderator just now?
    $context['report_sent'] = isset($_GET['reportsent']);
    // 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 = smf_db_query('
			SELECT
				lo.id_member, lo.log_time, mem.real_name, mem.member_name, mem.show_online, mem.id_group, mem.id_post_group
			FROM {db_prefix}log_online AS lo
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lo.id_member)
			WHERE INSTR(lo.url, {string:in_url_string}) > 0 OR lo.session = {string:session}', array('in_url_string' => 's:5:"topic";i:' . $topic . ';', 'session' => $user_info['is_guest'] ? 'ip' . $user_info['ip'] : session_id()));
        while ($row = mysql_fetch_assoc($request)) {
            if (empty($row['id_member'])) {
                continue;
            }
            $class = 'member group_' . (empty($row['id_group']) ? $row['id_post_group'] : $row['id_group']) . (in_array($row['id_member'], $user_info['buddies']) ? ' buddy' : '');
            $href = URL::user($row['id_member'], $row['real_name']);
            if ($row['id_member'] == $user_info['id']) {
                $link = '<strong>' . $txt['you'] . '</strong>';
            } else {
                $link = '<a onclick="getMcard(' . $row['id_member'] . ');return(false);" class="' . $class . '" href="' . $href . '">' . $row['real_name'] . '</a>';
            }
            // Add them both to the list and to the more detailed list.
            if (!empty($row['show_online']) || allowedTo('moderate_forum')) {
                $context['view_members_list'][$row['log_time'] . $row['member_name']] = empty($row['show_online']) ? '<em>' . $link . '</em>' : $link;
            }
            $context['view_members'][$row['log_time'] . $row['member_name']] = array('id' => $row['id_member'], 'username' => $row['member_name'], 'name' => $row['real_name'], 'group' => $row['id_group'], 'href' => $href, 'link' => $link, 'hidden' => empty($row['show_online']));
            if (empty($row['show_online'])) {
                $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.
    $can_show_all = !empty($modSettings['enableAllMessages']) && $context['total_visible_posts'] > $context['messages_per_page'] && $context['total_visible_posts'] < $modSettings['enableAllMessages'];
    if (isset($_REQUEST['all']) && !$can_show_all) {
        unset($_REQUEST['all']);
    } elseif (isset($_REQUEST['all'])) {
        $_REQUEST['start'] = -1;
    }
    // Construct the page index, allowing for the .START method...
    if (!isset($_REQUEST['perma'])) {
        $context['page_index'] = constructPageIndex(URL::topic($topic, $topicinfo['subject'], '%1$d'), $_REQUEST['start'], $context['total_visible_posts'], $context['messages_per_page'], 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'] / $context['messages_per_page'] + 1, 'num_pages' => floor(($context['total_visible_posts'] - 1) / $context['messages_per_page']) + 1);
    $context['links'] = array('first' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.0' : '', 'prev' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] - $context['messages_per_page']) : '', 'next' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] + $context['messages_per_page']) : '', 'last' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic . '.' . floor($context['total_visible_posts'] / $context['messages_per_page']) * $context['messages_per_page'] : '', 'up' => $scripturl . '?board=' . $board . '.0');
    // If they are viewing all the posts, show all the posts, otherwise limit the number.
    if ($can_show_all) {
        if (isset($_REQUEST['all'])) {
            // No limit! (actually, there is a limit, but...)
            $context['messages_per_page'] = -1;
            $context['page_index'] .= '[<strong>' . $txt['all'] . '</strong>] ';
            // Set start back to 0...
            $_REQUEST['start'] = 0;
        } else {
            if (!isset($context['page_index'])) {
                $context['page_index'] = '';
            }
            $context['page_index'] .= '&nbsp;<a href="' . $scripturl . '?topic=' . $topic . '.0;all">' . $txt['all'] . '</a> ';
        }
    }
    // Build the link tree.
    $context['linktree'][] = array('url' => URL::topic($topic, $topicinfo['subject'], 0), 'name' => $topicinfo['subject'], 'extra_before' => $settings['linktree_inline'] ? $txt['topic'] . ': ' : '');
    // 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['board_moderator'] . '">' . $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['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')';
    }
    // Information about the current topic...
    $context['is_locked'] = $topicinfo['locked'];
    $context['is_sticky'] = $topicinfo['is_sticky'];
    $context['is_very_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicVeryPosts'];
    $context['is_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicPosts'];
    $context['is_approved'] = $topicinfo['approved'];
    // 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'] = $user_info['id'] == $topicinfo['id_member_started'] && !$user_info['is_guest'];
    $context['topic_starter_id'] = $topicinfo['id_member_started'];
    // Set the topic's information for the template.
    $context['subject'] = $topicinfo['subject'];
    $context['num_views'] = $topicinfo['num_views'];
    $context['mark_unread_time'] = $topicinfo['new_from'];
    // Set a canonical URL for this page.
    $context['canonical_url'] = URL::topic($topic, $topicinfo['subject'], $context['start']);
    $context['share_url'] = $scripturl . '?topic=' . $topic;
    // For quick reply we need a response prefix in the default forum language.
    if (!isset($context['response_prefix']) && !($context['response_prefix'] = CacheAPI::getCache('response_prefix', 600))) {
        if ($language === $user_info['language']) {
            $context['response_prefix'] = $txt['response_prefix'];
        } else {
            loadLanguage('index', $language, false);
            $context['response_prefix'] = $txt['response_prefix'];
            loadLanguage('index');
        }
        CacheAPI::putCache('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 = smf_db_query('
			SELECT cal.id_event, cal.start_date, cal.end_date, cal.title, cal.id_member, mem.real_name
			FROM {db_prefix}calendar AS cal
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = cal.id_member)
			WHERE cal.id_topic = {int:current_topic}
			ORDER BY start_date', array('current_topic' => $topic));
        $context['linked_calendar_events'] = array();
        while ($row = mysql_fetch_assoc($request)) {
            // Prepare the dates for being formatted.
            $start_date = sscanf($row['start_date'], '%04d-%02d-%02d');
            $start_date = mktime(12, 0, 0, $start_date[1], $start_date[2], $start_date[0]);
            $end_date = sscanf($row['end_date'], '%04d-%02d-%02d');
            $end_date = mktime(12, 0, 0, $end_date[1], $end_date[2], $end_date[0]);
            $context['linked_calendar_events'][] = array('id' => $row['id_event'], 'title' => $row['title'], 'can_edit' => allowedTo('calendar_edit_any') || $row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own'), 'modify_href' => $scripturl . '?action=post;msg=' . $topicinfo['id_first_msg'] . ';topic=' . $topic . '.0;calendar;eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'start_date' => timeformat_static($start_date, $date_string, 'none'), 'start_timestamp' => $start_date, 'end_date' => timeformat_static($end_date, $date_string, 'none'), 'end_timestamp' => $end_date, '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 = smf_db_query('
			SELECT
				p.question, p.voting_locked, p.hide_results, p.expire_time, p.max_votes, p.change_vote,
				p.guest_vote, p.id_member, IFNULL(mem.real_name, p.poster_name) AS poster_name, p.num_guest_voters, p.reset_poll
			FROM {db_prefix}polls AS p
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = p.id_member)
			WHERE p.id_poll = {int:id_poll}
			LIMIT 1', array('id_poll' => $topicinfo['id_poll']));
        $pollinfo = mysql_fetch_assoc($request);
        mysql_free_result($request);
        $request = smf_db_query('
			SELECT COUNT(DISTINCT id_member) AS total
			FROM {db_prefix}log_polls
			WHERE id_poll = {int:id_poll}
				AND id_member != {int:not_guest}', array('id_poll' => $topicinfo['id_poll'], 'not_guest' => 0));
        list($pollinfo['total']) = mysql_fetch_row($request);
        mysql_free_result($request);
        // Total voters needs to include guest voters
        $pollinfo['total'] += $pollinfo['num_guest_voters'];
        // Get all the options, and calculate the total votes.
        $request = smf_db_query('
			SELECT pc.id_choice, pc.label, pc.votes, IFNULL(lp.id_choice, -1) AS voted_this
			FROM {db_prefix}poll_choices AS pc
				LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_choice = pc.id_choice AND lp.id_poll = {int:id_poll} AND lp.id_member = {int:current_member} AND lp.id_member != {int:not_guest})
			WHERE pc.id_poll = {int:id_poll}', array('current_member' => $user_info['id'], 'id_poll' => $topicinfo['id_poll'], 'not_guest' => 0));
        $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['voted_this'] != -1;
        }
        mysql_free_result($request);
        // If this is a guest we need to do our best to work out if they have voted, and what they voted for.
        if ($user_info['is_guest'] && $pollinfo['guest_vote'] && allowedTo('poll_vote')) {
            if (!empty($_COOKIE['guest_poll_vote']) && preg_match('~^[0-9,;]+$~', $_COOKIE['guest_poll_vote']) && strpos($_COOKIE['guest_poll_vote'], ';' . $topicinfo['id_poll'] . ',') !== false) {
                // ;id,timestamp,[vote,vote...]; etc
                $guestinfo = explode(';', $_COOKIE['guest_poll_vote']);
                // Find the poll we're after.
                foreach ($guestinfo as $i => $guestvoted) {
                    $guestvoted = explode(',', $guestvoted);
                    if ($guestvoted[0] == $topicinfo['id_poll']) {
                        break;
                    }
                }
                // Has the poll been reset since guest voted?
                if ($pollinfo['reset_poll'] > $guestvoted[1]) {
                    // Remove the poll info from the cookie to allow guest to vote again
                    unset($guestinfo[$i]);
                    if (!empty($guestinfo)) {
                        $_COOKIE['guest_poll_vote'] = ';' . implode(';', $guestinfo);
                    } else {
                        unset($_COOKIE['guest_poll_vote']);
                    }
                } else {
                    // What did they vote for?
                    unset($guestvoted[0], $guestvoted[1]);
                    foreach ($pollOptions as $choice => $details) {
                        $pollOptions[$choice]['voted_this'] = in_array($choice, $guestvoted) ? 1 : -1;
                        $pollinfo['has_voted'] |= $pollOptions[$choice]['voted_this'] != -1;
                    }
                    unset($choice, $details, $guestvoted);
                }
                unset($guestinfo, $guestvoted, $i);
            }
        }
        // Set up the basic poll information.
        $context['poll'] = array('id' => $topicinfo['id_poll'], 'image' => 'normal_' . (empty($pollinfo['voting_locked']) ? 'poll' : 'locked_poll'), 'question' => parse_bbc($pollinfo['question']), 'total_votes' => $pollinfo['total'], 'change_vote' => !empty($pollinfo['change_vote']), 'is_locked' => !empty($pollinfo['voting_locked']), '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['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($pollOptions), $pollinfo['max_votes'])) : '', 'is_expired' => !empty($pollinfo['expire_time']) && $pollinfo['expire_time'] < time(), 'expire_time' => !empty($pollinfo['expire_time']) ? timeformat($pollinfo['expire_time']) : 0, 'has_voted' => !empty($pollinfo['has_voted']), 'starter' => array('id' => $pollinfo['id_member'], 'name' => $row['poster_name'], 'href' => $pollinfo['id_member'] == 0 ? '' : $scripturl . '?action=profile;u=' . $pollinfo['id_member'], 'link' => $pollinfo['id_member'] == 0 ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $pollinfo['id_member'] . '">' . $row['poster_name'] . '</a>'));
        // Make the lock and edit permissions defined above more directly accessible.
        $context['allow_lock_poll'] = $context['poll']['lock'];
        $context['allow_edit_poll'] = $context['poll']['edit'];
        // You're allowed to vote if:
        // 1. the poll did not expire, and
        // 2. you're either not a guest OR guest voting is enabled... 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'] || $pollinfo['guest_vote'] && allowedTo('poll_vote')) && empty($pollinfo['voting_locked']) && 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 (hide_results == 0), or
        // 3. you can see them after you voted (hide_results == 1), or
        // 4. you've waited long enough for the poll to expire. (whether hide_results is 1 or 2.)
        $context['allow_poll_view'] = allowedTo('moderate_board') || $pollinfo['hide_results'] == 0 || $pollinfo['hide_results'] == 1 && $context['poll']['has_voted'] || $context['poll']['is_expired'];
        $context['poll']['show_results'] = $context['allow_poll_view'] && (isset($_REQUEST['viewresults']) || isset($_REQUEST['viewResults']));
        $context['show_view_results_button'] = $context['allow_vote'] && (!$context['allow_poll_view'] || !$context['poll']['show_results'] || !$context['poll']['has_voted']);
        // 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['voting_locked']) && allowedTo('poll_vote') && $context['poll']['has_voted'] && $context['poll']['change_vote'];
        // You're allowed to return to voting options if:
        // 1. you are (still) allowed to vote.
        // 2. you are currently seeing the results.
        $context['allow_return_vote'] = $context['allow_vote'] && $context['poll']['show_results'];
        // 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['voted_this'] != -1, 'bar' => '<span style="white-space: nowrap;"><img src="' . $settings['images_url'] . '/poll_' . ($context['right_to_left'] ? 'right' : 'left') . '.gif" alt="" /><img src="' . $settings['images_url'] . '/poll_middle.gif" width="' . $barWide . '" height="12" alt="-" /><img src="' . $settings['images_url'] . '/poll_' . ($context['right_to_left'] ? 'left' : 'right') . '.gif" alt="" /></span>', 'bar_ndt' => $bar > 0 ? '<div class="bar" style="width: ' . ($bar * 3.5 + 4) . 'px;"></div>' : '', 'bar_width' => $barWide, 'option' => parse_bbc($option['label']), 'vote_button' => '<input type="' . ($pollinfo['max_votes'] > 1 ? 'checkbox' : 'radio') . '" name="options[]" id="options-' . $i . '" value="' . $i . '" class="input_' . ($pollinfo['max_votes'] > 1 ? 'check' : 'radio') . '" />');
        }
    }
    // Calculate the fastest way to get the messages!
    $ascending = empty($options['view_newest_first']);
    $start = $_REQUEST['start'];
    $limit = $context['messages_per_page'];
    $firstIndex = 0;
    if ($start >= $context['total_visible_posts'] / 2 && $context['messages_per_page'] != -1) {
        $ascending = !$ascending;
        $limit = $context['total_visible_posts'] <= $start + $limit ? $context['total_visible_posts'] - $start : $limit;
        $start = $context['total_visible_posts'] <= $start + $limit ? 0 : $context['total_visible_posts'] - $start - $limit;
        $firstIndex = $limit - 1;
    }
    if (!isset($_REQUEST['perma'])) {
        // Get each post and poster in this topic.
        $request = smf_db_query('
			SELECT id_msg, id_member, approved
			FROM {db_prefix}messages
			WHERE id_topic = {int:current_topic}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : (!empty($modSettings['db_mysql_group_by_fix']) ? '' : '
			GROUP BY id_msg') . '
			HAVING (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')') . '
			ORDER BY id_msg ' . ($ascending ? '' : 'DESC') . ($context['messages_per_page'] == -1 ? '' : '
			LIMIT ' . $start . ', ' . $limit), array('current_member' => $user_info['id'], 'current_topic' => $topic, 'is_approved' => 1, 'blank_id_member' => 0));
        $messages = array();
        $all_posters = array();
        while ($row = mysql_fetch_assoc($request)) {
            if (!empty($row['id_member'])) {
                $all_posters[$row['id_msg']] = $row['id_member'];
            }
            $messages[] = $row['id_msg'];
        }
        mysql_free_result($request);
        $posters[$context['topic_first_message']] = $context['topic_starter_id'];
        $posters = array_unique($all_posters);
    } else {
        $request = smf_db_query('
			SELECT id_member, approved
			FROM {db_prefix}messages
			WHERE id_msg = {int:id_msg}', array('id_msg' => $virtual_msg));
        list($id_member, $approved) = mysql_fetch_row($request);
        mysql_free_result($request);
        EoS_Smarty::loadTemplate('topic/topic_singlepost');
        //loadTemplate('DisplaySingle');
        $context['sub_template'] = isset($_REQUEST['xml']) ? 'single_post_xml' : 'single_post';
        if (isset($_REQUEST['xml'])) {
            $context['template_layers'] = array();
            header('Content-Type: text/xml; charset=UTF-8');
        }
        $messages = array($virtual_msg);
        $posters[$virtual_msg] = $id_member;
    }
    // Guests can't mark topics read or for notifications, just can't sorry.
    if (!$user_info['is_guest']) {
        $mark_at_msg = max($messages);
        if ($mark_at_msg >= $topicinfo['id_last_msg']) {
            $mark_at_msg = $modSettings['maxMsgID'];
        }
        if ($mark_at_msg >= $topicinfo['new_from']) {
            smf_db_insert($topicinfo['new_from'] == 0 ? 'ignore' : 'replace', '{db_prefix}log_topics', array('id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int'), array($user_info['id'], $topic, $mark_at_msg), array('id_member', 'id_topic'));
        }
        // Check for notifications on this topic OR board.
        $request = smf_db_query('
			SELECT sent, id_topic
			FROM {db_prefix}log_notify
			WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board})
				AND id_member = {int:current_member}
			LIMIT 2', array('current_board' => $board, 'current_member' => $user_info['id'], 'current_topic' => $topic));
        $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) {
                smf_db_query('
					UPDATE {db_prefix}log_notify
					SET sent = {int:is_not_sent}
					WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board})
						AND id_member = {int:current_member}', array('current_board' => $board, 'current_member' => $user_info['id'], 'current_topic' => $topic, 'is_not_sent' => 0));
                $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 = smf_db_query('
				SELECT COUNT(*)
				FROM {db_prefix}topics AS t
					LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = {int:current_board} AND lb.id_member = {int:current_member})
					LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
				WHERE t.id_board = {int:current_board}
					AND t.id_last_msg > IFNULL(lb.id_msg, 0)
					AND t.id_last_msg > IFNULL(lt.id_msg, 0)' . (empty($_SESSION['id_msg_last_visit']) ? '' : '
					AND t.id_last_msg > {int:id_msg_last_visit}'), array('current_board' => $board, 'current_member' => $user_info['id'], 'id_msg_last_visit' => (int) $_SESSION['id_msg_last_visit']));
            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'])) {
            smf_db_insert('replace', '{db_prefix}log_boards', array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'), array($modSettings['maxMsgID'], $user_info['id'], $board), array('id_member', 'id_board'));
        }
    }
    $attachments = array();
    // deal with possible sticky posts and different postbit layouts for
    // the first post
    // topic.id_layout meanings: bit 0-6 > layout id, bit 7 > first post sticky on every page.
    // don't blame me for using bit magic here. I'm a C guy and a 8bits can store more than just one bool :P
    $layout = (int) ($topicinfo['id_layout'] & 0x7f);
    $postbit_classes =& EoS_Smarty::getConfigInstance()->getPostbitClasses();
    // set defaults...
    $context['postbit_callbacks'] = array('firstpost' => 'template_postbit_normal', 'post' => 'template_postbit_normal');
    $context['postbit_template_class'] = array('firstpost' => $postbit_classes['normal'], 'post' => $postbit_classes['normal']);
    if ($topicinfo['id_layout']) {
        $this_start = isset($_REQUEST['perma']) ? 0 : (int) $_REQUEST['start'];
        if ((int) $topicinfo['id_layout'] & 0x80) {
            if ($this_start > 0) {
                array_unshift($messages, intval($topicinfo['id_first_msg']));
            }
            $context['postbit_callbacks']['firstpost'] = $layout == 0 ? 'template_postbit_normal' : ($layout == 2 ? 'template_postbit_clean' : 'template_postbit_lean');
            $context['postbit_callbacks']['post'] = $layout == 2 ? 'template_postbit_comment' : 'template_postbit_normal';
            $context['postbit_template_class']['firstpost'] = $layout == 0 ? $postbit_classes['normal'] : ($layout == 2 ? $postbit_classes['article'] : $postbit_classes['lean']);
            $context['postbit_template_class']['post'] = $layout == 2 ? $postbit_classes['commentstyle'] : $postbit_classes['normal'];
        } elseif ($layout) {
            $context['postbit_callbacks']['firstpost'] = $layout == 0 || $this_start != 0 ? 'template_postbit_normal' : ($layout == 2 ? 'template_postbit_clean' : 'template_postbit_lean');
            $context['postbit_callbacks']['post'] = $layout == 2 ? 'template_postbit_comment' : 'template_postbit_normal';
            $context['postbit_template_class']['firstpost'] = $layout == 0 || $this_start != 0 ? $postbit_classes['normal'] : ($layout == 2 ? $postbit_classes['article'] : $postbit_classes['lean']);
            $context['postbit_template_class']['post'] = $layout == 2 ? $postbit_classes['commentstyle'] : $postbit_classes['normal'];
        }
    }
    // now we know which display template we need
    if (!isset($_REQUEST['perma'])) {
        EoS_Smarty::loadTemplate($layout > 1 ? 'topic/topic_page' : 'topic/topic');
    }
    /*
    if($user_info['is_admin']) {
    	EoS_Smarty::init();
    	if(!isset($_REQUEST['perma']))
    		EoS_Smarty::loadTemplate($layout > 1 ? 'topic_page' : 'topic');
    }
    else {
    	if(!isset($_REQUEST['perma']))
    		loadTemplate($layout > 1 ? 'DisplayPage' : 'Display');
    	loadTemplate('Postbit');
    }
    */
    // If there _are_ messages here... (probably an error otherwise :!)
    if (!empty($messages)) {
        // Fetch attachments.
        if (!empty($modSettings['attachmentEnable']) && allowedTo('view_attachments')) {
            $request = smf_db_query('
				SELECT
					a.id_attach, a.id_folder, a.id_msg, a.filename, a.file_hash, IFNULL(a.size, 0) AS filesize, a.downloads, a.approved,
					a.width, a.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ',
					IFNULL(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . '
				FROM {db_prefix}attachments AS a' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : '
					LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb)') . '
				WHERE a.id_msg IN ({array_int:message_list})
					AND a.attachment_type = {int:attachment_type}', array('message_list' => $messages, 'attachment_type' => 0, 'is_approved' => 1));
            $temp = array();
            while ($row = mysql_fetch_assoc($request)) {
                if (!$row['approved'] && $modSettings['postmod_active'] && !allowedTo('approve_posts') && (!isset($all_posters[$row['id_msg']]) || $all_posters[$row['id_msg']] != $user_info['id'])) {
                    continue;
                }
                $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 (!isset($posters[$context['topic_starter_id']])) {
            $posters[] = $context['topic_starter_id'];
        }
        if (!empty($posters)) {
            loadMemberData($posters);
        }
        if (!isset($user_profile[$context['topic_starter_id']])) {
            $context['topicstarter']['name'] = $topicinfo['poster_name'];
            $context['topicstarter']['id'] = 0;
            $context['topicstarter']['group'] = $txt['guest_title'];
            $context['topicstarter']['link'] = $topicinfo['poster_name'];
            $context['topicstarter']['email'] = $topicinfo['poster_email'];
            $context['topicstarter']['show_email'] = showEmailAddress(true, 0);
            $context['topicstarter']['is_guest'] = true;
            $context['topicstarter']['avatar'] = array();
        } else {
            loadMemberContext($context['topic_starter_id'], true);
            $context['topicstarter'] =& $memberContext[$context['topic_starter_id']];
        }
        $context['topicstarter']['start_time'] = timeformat($topicinfo['first_post_time']);
        $sql_what = '
			m.id_msg, m.icon, m.subject, m.poster_time, m.poster_ip, m.id_member, m.modified_time, m.modified_name, m.body, mc.body AS cached_body,
			m.smileys_enabled, m.poster_name, m.poster_email, m.approved, m.locked,' . (!empty($modSettings['karmaMode']) ? 'c.likes_count, c.like_status, c.updated AS like_updated, l.rtype AS liked,' : '0 AS likes_count, 0 AS like_status, 0 AS like_updated, 0 AS liked,') . '
			m.id_msg_modified < {int:new_from} AS is_read';
        $sql_from_tables = '
			FROM {db_prefix}messages AS m';
        $sql_from_joins = (!empty($modSettings['karmaMode']) ? '
			LEFT JOIN {db_prefix}likes AS l ON (l.id_msg = m.id_msg AND l.ctype = 1 AND l.id_user = {int:id_user})
			LEFT JOIN {db_prefix}like_cache AS c ON (c.id_msg = m.id_msg AND c.ctype = 1)' : '') . '
			LEFT JOIN {db_prefix}messages_cache AS mc on mc.id_msg = m.id_msg AND mc.style = {int:style} AND mc.lang = {int:lang}';
        $sql_array = array('message_list' => $messages, 'new_from' => $topicinfo['new_from'], 'style' => $user_info['smiley_set_id'], 'lang' => $user_info['language_id'], 'id_user' => $user_info['id']);
        HookAPI::callHook('display_messagerequest', array(&$sql_what, &$sql_from_tables, &$sql_from_joins, &$sql_array));
        $messages_request = smf_db_query('
			SELECT ' . $sql_what . ' ' . $sql_from_tables . $sql_from_joins . '
			WHERE m.id_msg IN ({array_int:message_list})
			ORDER BY m.id_msg' . (empty($options['view_newest_first']) ? '' : ' DESC'), $sql_array);
        // Go to the last message if the given time is beyond the time of the last message.
        if (isset($context['start_from']) && $context['start_from'] >= $topicinfo['num_replies']) {
            $context['start_from'] = $topicinfo['num_replies'];
        }
        // Since the anchor information is needed on the top of the page we load these variables beforehand.
        $context['first_message'] = isset($messages[$firstIndex]) ? $messages[$firstIndex] : $messages[0];
        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['num_replies'] - $context['start_from'];
        }
    } else {
        $messages_request = false;
        $context['first_message'] = 0;
        $context['first_new_message'] = false;
    }
    $context['jump_to'] = array('label' => addslashes(un_htmlspecialchars($txt['jump_to'])), 'board_name' => htmlspecialchars(strtr(strip_tags($board_info['name']), array('&amp;' => '&'))), 'child_level' => $board_info['child_level']);
    // Set the callback.  (do you REALIZE how much memory all the messages would take?!?)
    $context['get_message'] = 'prepareDisplayContext';
    // Now set all the wonderful, wonderful permissions... like moderation ones...
    $common_permissions = array('can_approve' => 'approve_posts', 'can_ban' => 'manage_bans', 'can_sticky' => 'make_sticky', 'can_merge' => 'merge_any', 'can_split' => 'split_any', 'calendar_post' => 'calendar_post', 'can_mark_notify' => 'mark_any_notify', 'can_send_topic' => 'send_topic', 'can_send_pm' => 'pm_send', 'can_report_moderator' => 'report_any', 'can_moderate_forum' => 'moderate_forum', 'can_issue_warning' => 'issue_warning', 'can_restore_topic' => 'move_any', 'can_restore_msg' => 'move_any');
    foreach ($common_permissions as $contextual => $perm) {
        $context[$contextual] = allowedTo($perm);
    }
    // Permissions with _any/_own versions.  $context[YYY] => ZZZ_any/_own.
    $anyown_permissions = array('can_move' => 'move', 'can_lock' => 'lock', 'can_delete' => 'remove', 'can_add_poll' => 'poll_add', 'can_remove_poll' => 'poll_remove', 'can_reply' => 'post_reply', 'can_reply_unapproved' => 'post_unapproved_replies');
    foreach ($anyown_permissions as $contextual => $perm) {
        $context[$contextual] = allowedTo($perm . '_any') || $context['user']['started'] && allowedTo($perm . '_own');
    }
    $context['can_add_tags'] = $context['user']['started'] && allowedTo('smftags_add') || allowedTo('smftags_manage');
    $context['can_delete_tags'] = $context['user']['started'] && allowedTo('smftags_del') || allowedTo('smftags_manage');
    $context['can_moderate_board'] = allowedTo('moderate_board');
    $context['can_modify_any'] = allowedTo('modify_any');
    $context['can_modify_replies'] = allowedTo('modify_replies');
    $context['can_modify_own'] = allowedTo('modify_own');
    $context['can_delete_any'] = allowedTo('delete_any');
    $context['can_delete_replies'] = allowedTo('delete_replies');
    $context['can_delete_own'] = allowedTo('delete_own');
    $context['use_share'] = !$user_info['possibly_robot'] && allowedTo('use_share') && ($context['user']['is_guest'] || (empty($options['use_share_bar']) ? 1 : !$options['use_share_bar']));
    $context['can_unapprove'] = $context['can_approve'] && !empty($modSettings['postmod_active']);
    $context['can_profile_view_any'] = allowedTo('profile_view_any');
    $context['can_profile_view_own'] = allowedTo('profile_view_own');
    $context['is_banned_from_topic'] = !$user_info['is_admin'] && !$context['can_moderate_forum'] && !$context['can_moderate_board'] && (!empty($context['topic_banned_members']) ? in_array($user_info['id'], $context['topic_banned_members']) : false);
    $context['banned_notice'] = $context['is_banned_from_topic'] ? $txt['topic_banned_notice'] : '';
    // 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');
    $context['can_reply_unapproved'] &= $modSettings['postmod_active'] && (empty($topicinfo['locked']) || allowedTo('moderate_board'));
    $context['can_issue_warning'] &= in_array('w', $context['admin_features']) && $modSettings['warning_settings'][0] == 1;
    // Handle approval flags...
    $context['can_reply_approved'] = $context['can_reply'];
    $context['can_reply'] |= $context['can_reply_unapproved'];
    $context['can_quote'] = $context['can_reply'] && (empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])));
    $context['can_mark_unread'] = !$user_info['is_guest'] && $settings['show_mark_read'];
    $context['can_send_topic'] = (!$modSettings['postmod_active'] || $topicinfo['approved']) && allowedTo('send_topic');
    // Start this off for quick moderation - it will be or'd for each post.
    $context['can_remove_post'] = allowedTo('delete_any') || allowedTo('delete_replies') && $context['user']['started'];
    // Can restore topic?  That's if the topic is in the recycle board and has a previous restore state.
    $context['can_restore_topic'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_board']);
    $context['can_restore_msg'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_topic']);
    if ($context['is_banned_from_topic']) {
        $context['can_add_tags'] = $context['can_delete_tags'] = $context['can_modify_any'] = $context['can_modify_replies'] = $context['can_modify_own'] = $context['can_delete_any'] = $context['can_delete_replies'] = $context['can_delete_own'] = $context['can_lock'] = $context['can_sticky'] = $context['calendar_post'] = $context['can_add_poll'] = $context['can_remove_poll'] = $context['can_reply'] = $context['can_reply_unapproved'] = $context['can_quote'] = $context['can_remove_post'] = false;
    }
    // Load up the "double post" sequencing magic.
    if (!empty($options['display_quick_reply'])) {
        checkSubmitOnce('register');
        $context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
        $context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
    }
    // todo: drafts -> plugin
    $context['can_save_draft'] = false;
    //$context['can_reply'] && !$context['user']['is_guest'] && in_array('dr', $context['admin_features']) && !empty($options['use_drafts']) && allowedTo('drafts_allow');
    $context['can_autosave_draft'] = false;
    //$context['can_save_draft'] && !empty($modSettings['enableAutoSaveDrafts']) && allowedTo('drafts_autosave_allow');
    enqueueThemeScript('topic', 'scripts/topic.js', true);
    if ($context['can_autosave_draft']) {
        enqueueThemeScript('drafts', 'scripts/drafts.js', true);
    }
    $context['can_moderate_member'] = $context['can_issue_warning'] || $context['can_moderate_board'];
    $context['topic_has_banned_members_msg'] = $context['topic_banned_members_count'] > 0 && $context['can_moderate_board'] ? sprintf($txt['topic_has_bans_msg'], URL::parse('?action=moderate;area=topicbans;sa=bytopic;t=' . $topic)) : '';
    if (EoS_Smarty::isActive()) {
        if (isset($context['poll'])) {
            $context['poll_buttons'] = array('vote' => array('test' => 'allow_return_vote', 'text' => 'poll_return_vote', 'image' => 'poll_options.gif', 'lang' => true, 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start']), 'results' => array('test' => 'show_view_results_button', 'text' => 'poll_results', 'image' => 'poll_results.gif', 'lang' => true, 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start'] . ';viewresults'), 'change_vote' => array('test' => 'allow_change_vote', 'text' => 'poll_change_vote', 'image' => 'poll_change_vote.gif', 'lang' => true, 'url' => $scripturl . '?action=vote;topic=' . $context['current_topic'] . '.' . $context['start'] . ';poll=' . $context['poll']['id'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'lock' => array('test' => 'allow_lock_poll', 'text' => !$context['poll']['is_locked'] ? 'poll_lock' : 'poll_unlock', 'image' => 'poll_lock.gif', 'lang' => true, 'url' => $scripturl . '?action=lockvoting;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'edit' => array('test' => 'allow_edit_poll', 'text' => 'poll_edit', 'image' => 'poll_edit.gif', 'lang' => true, 'url' => $scripturl . '?action=editpoll;topic=' . $context['current_topic'] . '.' . $context['start']), 'remove_poll' => array('test' => 'can_remove_poll', 'text' => 'poll_remove', 'image' => 'admin_remove_poll.gif', 'lang' => true, 'custom' => 'onclick="return Eos_Confirm(\'\', \'' . $txt['poll_remove_warn'] . '\', $(this).attr(\'href\'));"', 'url' => $scripturl . '?action=removepoll;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']));
        }
        $context['normal_buttons'] = array('reply' => array('test' => 'can_reply', 'text' => 'reply', 'custom' => 'onclick="return oQuickReply.quote(0);" ', 'image' => 'reply.gif', 'lang' => true, 'url' => $scripturl . '?action=post;topic=' . $context['current_topic'] . '.' . $context['start'] . ';last_msg=' . $context['topic_last_message'], 'active' => true), 'add_poll' => array('test' => 'can_add_poll', 'text' => 'add_poll', 'image' => 'add_poll.gif', 'lang' => true, 'url' => $scripturl . '?action=editpoll;add;topic=' . $context['current_topic'] . '.' . $context['start']), 'mark_unread' => array('test' => 'can_mark_unread', 'text' => 'mark_unread', 'image' => 'markunread.gif', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=topic;t=' . $context['mark_unread_time'] . ';topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']));
        HookAPI::callHook('integrate_display_buttons', array(&$context['normal_buttons']));
        $remove_url = $scripturl . '?action=removetopic2;topic=' . $context['current_topic'] . '.0;' . $context['session_var'] . '=' . $context['session_id'];
        $context['mod_buttons'] = array('move' => array('test' => 'can_move', 'text' => 'move_topic', 'image' => 'admin_move.gif', 'lang' => true, 'url' => $scripturl . '?action=movetopic;topic=' . $context['current_topic'] . '.0'), 'delete' => array('test' => 'can_delete', 'text' => 'remove_topic', 'image' => 'admin_rem.gif', 'lang' => true, 'custom' => 'onclick="return Eos_Confirm(\'\',\'' . $txt['are_sure_remove_topic'] . '\',\'' . $remove_url . '\');"', 'url' => $remove_url), 'lock' => array('test' => 'can_lock', 'text' => empty($context['is_locked']) ? 'set_lock' : 'set_unlock', 'image' => 'admin_lock.gif', 'lang' => true, 'url' => $scripturl . '?action=lock;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'sticky' => array('test' => 'can_sticky', 'text' => empty($context['is_sticky']) ? 'set_sticky' : 'set_nonsticky', 'image' => 'admin_sticky.gif', 'lang' => true, 'url' => $scripturl . '?action=sticky;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'merge' => array('test' => 'can_merge', 'text' => 'merge', 'image' => 'merge.gif', 'lang' => true, 'url' => $scripturl . '?action=mergetopics;board=' . $context['current_board'] . '.0;from=' . $context['current_topic']), 'calendar' => array('test' => 'calendar_post', 'text' => 'calendar_link', 'image' => 'linktocal.gif', 'lang' => true, 'url' => $scripturl . '?action=post;calendar;msg=' . $context['topic_first_message'] . ';topic=' . $context['current_topic'] . '.0'));
        // Restore topic. eh?  No monkey business.
        if ($context['can_restore_topic']) {
            $context['mod_buttons'][] = array('text' => 'restore_topic', 'image' => '', 'lang' => true, 'url' => $scripturl . '?action=restoretopic;topics=' . $context['current_topic'] . ';' . $context['session_var'] . '=' . $context['session_id']);
        }
        // Allow adding new mod buttons easily.
        HookAPI::callHook('integrate_mod_buttons', array(&$context['mod_buttons']));
        $context['message_ids'] = $messages;
        $context['perma_request'] = isset($_REQUEST['perma']) ? true : false;
        $context['mod_buttons_style'] = array('id' => 'moderationbuttons_strip', 'class' => 'buttonlist');
        $context['full_members_viewing_list'] = empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . (empty($context['view_num_hidden']) || $context['can_moderate_forum'] ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')');
    }
    fetchNewsItems($board, $topic);
    HookAPI::callHook('display_general', array());
    /*
     * $message is always available in templates as global variable
     * prepareDisplayContext() just repopulates it and is called from
     * the topic display template via $SUPPORT object callback.
     */
    EoS_Smarty::getSmartyInstance()->assignByRef('message', $output);
}
Beispiel #12
0
    /**
     * Posts or saves the message composed with Post().
     *
     * requires various permissions depending on the action.
     * handles attachment, post, and calendar saving.
     * sends off notifications, and allows for announcements and moderation.
     * accessed from ?action=post2.
     */
    public function action_post2()
    {
        global $board, $topic, $txt, $modSettings, $context, $user_settings;
        global $user_info, $board_info, $options, $ignore_temp;
        // Sneaking off, are we?
        if (empty($_POST) && empty($topic)) {
            if (empty($_SERVER['CONTENT_LENGTH'])) {
                redirectexit('action=post;board=' . $board . '.0');
            } else {
                fatal_lang_error('post_upload_error', false);
            }
        } elseif (empty($_POST) && !empty($topic)) {
            redirectexit('action=post;topic=' . $topic . '.0');
        }
        // No need!
        $context['robot_no_index'] = true;
        // We are now in post2 action
        $context['current_action'] = 'post2';
        require_once SOURCEDIR . '/AttachmentErrorContext.class.php';
        // No errors as yet.
        $post_errors = Error_Context::context('post', 1);
        $attach_errors = Attachment_Error_Context::context();
        // If the session has timed out, let the user re-submit their form.
        if (checkSession('post', '', false) != '') {
            $post_errors->addError('session_timeout');
            // Disable the preview so that any potentially malicious code is not executed
            $_REQUEST['preview'] = false;
            return $this->action_post();
        }
        // Wrong verification code?
        if (!$user_info['is_admin'] && !$user_info['is_mod'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || $user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1)) {
            require_once SUBSDIR . '/VerificationControls.class.php';
            $verificationOptions = array('id' => 'post');
            $context['require_verification'] = create_control_verification($verificationOptions, true);
            if (is_array($context['require_verification'])) {
                foreach ($context['require_verification'] as $verification_error) {
                    $post_errors->addError($verification_error);
                }
            }
        }
        require_once SUBSDIR . '/Boards.subs.php';
        require_once SUBSDIR . '/Post.subs.php';
        loadLanguage('Post');
        // Drafts enabled and needed?
        if (!empty($modSettings['drafts_enabled']) && (isset($_POST['save_draft']) || isset($_POST['id_draft']))) {
            require_once SUBSDIR . '/Drafts.subs.php';
        }
        // First check to see if they are trying to delete any current attachments.
        if (isset($_POST['attach_del'])) {
            $keep_temp = array();
            $keep_ids = array();
            foreach ($_POST['attach_del'] as $dummy) {
                if (strpos($dummy, 'post_tmp_' . $user_info['id']) !== false) {
                    $keep_temp[] = $dummy;
                } else {
                    $keep_ids[] = (int) $dummy;
                }
            }
            if (isset($_SESSION['temp_attachments'])) {
                foreach ($_SESSION['temp_attachments'] as $attachID => $attachment) {
                    if (isset($_SESSION['temp_attachments']['post']['files'], $attachment['name']) && in_array($attachment['name'], $_SESSION['temp_attachments']['post']['files']) || in_array($attachID, $keep_temp) || strpos($attachID, 'post_tmp_' . $user_info['id']) === false) {
                        continue;
                    }
                    unset($_SESSION['temp_attachments'][$attachID]);
                    @unlink($attachment['tmp_name']);
                }
            }
            if (!empty($_REQUEST['msg'])) {
                require_once SUBSDIR . '/ManageAttachments.subs.php';
                $attachmentQuery = array('attachment_type' => 0, 'id_msg' => (int) $_REQUEST['msg'], 'not_id_attach' => $keep_ids);
                removeAttachments($attachmentQuery);
            }
        }
        // Then try to upload any attachments.
        $context['attachments']['can']['post'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || $modSettings['postmod_active'] && allowedTo('post_unapproved_attachments'));
        if ($context['attachments']['can']['post'] && empty($_POST['from_qr'])) {
            require_once SUBSDIR . '/Attachments.subs.php';
            if (isset($_REQUEST['msg'])) {
                processAttachments((int) $_REQUEST['msg']);
            } else {
                processAttachments();
            }
        }
        // Previewing? Go back to start.
        if (isset($_REQUEST['preview'])) {
            return $this->action_post();
        }
        // Prevent double submission of this form.
        checkSubmitOnce('check');
        // If this isn't a new topic load the topic info that we need.
        if (!empty($topic)) {
            require_once SUBSDIR . '/Topic.subs.php';
            $topic_info = getTopicInfo($topic);
            // Though the topic should be there, it might have vanished.
            if (empty($topic_info)) {
                fatal_lang_error('topic_doesnt_exist');
            }
            // Did this topic suddenly move? Just checking...
            if ($topic_info['id_board'] != $board) {
                fatal_lang_error('not_a_topic');
            }
        }
        // Replying to a topic?
        if (!empty($topic) && !isset($_REQUEST['msg'])) {
            // Don't allow a post if it's locked.
            if ($topic_info['locked'] != 0 && !allowedTo('moderate_board')) {
                fatal_lang_error('topic_locked', false);
            }
            // Sorry, multiple polls aren't allowed... yet.  You should stop giving me ideas :P.
            if (isset($_REQUEST['poll']) && $topic_info['id_poll'] > 0) {
                unset($_REQUEST['poll']);
            }
            // Do the permissions and approval stuff...
            $becomesApproved = true;
            if ($topic_info['id_member_started'] != $user_info['id']) {
                if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any')) {
                    $becomesApproved = false;
                } else {
                    isAllowedTo('post_reply_any');
                }
            } elseif (!allowedTo('post_reply_any')) {
                if ($modSettings['postmod_active']) {
                    if (allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own')) {
                        $becomesApproved = false;
                    } elseif ($user_info['is_guest'] && allowedTo('post_unapproved_replies_any')) {
                        $becomesApproved = false;
                    } else {
                        isAllowedTo('post_reply_own');
                    }
                }
            }
            if (isset($_POST['lock'])) {
                // Nothing is changed to the lock.
                if (empty($topic_info['locked']) && empty($_POST['lock']) || !empty($_POST['lock']) && !empty($topic_info['locked'])) {
                    unset($_POST['lock']);
                } elseif (!allowedTo(array('lock_any', 'lock_own')) || !allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']) {
                    unset($_POST['lock']);
                } elseif (!allowedTo('lock_any')) {
                    // You cannot override a moderator lock.
                    if ($topic_info['locked'] == 1) {
                        unset($_POST['lock']);
                    } else {
                        $_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
                    }
                } else {
                    $_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
                }
            }
            // So you wanna (un)sticky this...let's see.
            if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || $_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky'))) {
                unset($_POST['sticky']);
            }
            // If drafts are enabled, then pass this off
            if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft'])) {
                saveDraft();
                return $this->action_post();
            }
            // If the number of replies has changed, if the setting is enabled, go back to action_post() - which handles the error.
            if (empty($options['no_new_reply_warning']) && isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg']) {
                addInlineJavascript('
					$(document).ready(function () {
						$("html,body").scrollTop($(\'.category_header:visible:first\').offset().top);
					});');
                return $this->action_post();
            }
            $posterIsGuest = $user_info['is_guest'];
        } elseif (empty($topic)) {
            // Now don't be silly, new topics will get their own id_msg soon enough.
            unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
            // Do like, the permissions, for safety and stuff...
            $becomesApproved = true;
            if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics')) {
                $becomesApproved = false;
            } else {
                isAllowedTo('post_new');
            }
            if (isset($_POST['lock'])) {
                // New topics are by default not locked.
                if (empty($_POST['lock'])) {
                    unset($_POST['lock']);
                } elseif (!allowedTo(array('lock_any', 'lock_own'))) {
                    unset($_POST['lock']);
                } else {
                    $_POST['lock'] = allowedTo('lock_any') ? 1 : 2;
                }
            }
            if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || empty($_POST['sticky']) || !allowedTo('make_sticky'))) {
                unset($_POST['sticky']);
            }
            // Saving your new topic as a draft first?
            if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft'])) {
                saveDraft();
                return $this->action_post();
            }
            $posterIsGuest = $user_info['is_guest'];
        } elseif (isset($_REQUEST['msg']) && !empty($topic)) {
            $_REQUEST['msg'] = (int) $_REQUEST['msg'];
            require_once SUBSDIR . '/Messages.subs.php';
            $msgInfo = basicMessageInfo($_REQUEST['msg'], true);
            if (empty($msgInfo)) {
                fatal_lang_error('cant_find_messages', false);
            }
            if (!empty($topic_info['locked']) && !allowedTo('moderate_board')) {
                fatal_lang_error('topic_locked', false);
            }
            if (isset($_POST['lock'])) {
                // Nothing changes to the lock status.
                if (empty($_POST['lock']) && empty($topic_info['locked']) || !empty($_POST['lock']) && !empty($topic_info['locked'])) {
                    unset($_POST['lock']);
                } elseif (!allowedTo(array('lock_any', 'lock_own')) || !allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']) {
                    unset($_POST['lock']);
                } elseif (!allowedTo('lock_any')) {
                    // You're not allowed to break a moderator's lock.
                    if ($topic_info['locked'] == 1) {
                        unset($_POST['lock']);
                    } else {
                        $_POST['lock'] = empty($_POST['lock']) ? 0 : 2;
                    }
                } else {
                    $_POST['lock'] = empty($_POST['lock']) ? 0 : 1;
                }
            }
            // Change the sticky status of this topic?
            if (isset($_POST['sticky']) && (!allowedTo('make_sticky') || $_POST['sticky'] == $topic_info['is_sticky'])) {
                unset($_POST['sticky']);
            }
            if ($msgInfo['id_member'] == $user_info['id'] && !allowedTo('modify_any')) {
                if ((!$modSettings['postmod_active'] || $msgInfo['approved']) && !empty($modSettings['edit_disable_time']) && $msgInfo['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time()) {
                    fatal_lang_error('modify_post_time_passed', false);
                } elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_own')) {
                    isAllowedTo('modify_replies');
                } else {
                    isAllowedTo('modify_own');
                }
            } elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_any')) {
                isAllowedTo('modify_replies');
                // If you're modifying a reply, I say it better be logged...
                $moderationAction = true;
            } else {
                isAllowedTo('modify_any');
                // Log it, assuming you're not modifying your own post.
                if ($msgInfo['id_member'] != $user_info['id']) {
                    $moderationAction = true;
                }
            }
            // If drafts are enabled, then lets send this off to save
            if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft'])) {
                saveDraft();
                return $this->action_post();
            }
            $posterIsGuest = empty($msgInfo['id_member']);
            // Can they approve it?
            $can_approve = allowedTo('approve_posts');
            $becomesApproved = $modSettings['postmod_active'] ? $can_approve && !$msgInfo['approved'] ? !empty($_REQUEST['approve']) ? 1 : 0 : $msgInfo['approved'] : 1;
            $approve_has_changed = $msgInfo['approved'] != $becomesApproved;
            if (!allowedTo('moderate_forum') || !$posterIsGuest) {
                $_POST['guestname'] = $msgInfo['poster_name'];
                $_POST['email'] = $msgInfo['poster_email'];
            }
        }
        // In case we want to override
        if (allowedTo('approve_posts')) {
            $becomesApproved = !isset($_REQUEST['approve']) || !empty($_REQUEST['approve']) ? 1 : 0;
            $approve_has_changed = isset($msgInfo['approved']) ? $msgInfo['approved'] != $becomesApproved : false;
        }
        // If the poster is a guest evaluate the legality of name and email.
        if ($posterIsGuest) {
            $_POST['guestname'] = !isset($_POST['guestname']) ? '' : Util::htmlspecialchars(trim($_POST['guestname']));
            $_POST['email'] = !isset($_POST['email']) ? '' : Util::htmlspecialchars(trim($_POST['email']));
            if ($_POST['guestname'] == '' || $_POST['guestname'] == '_') {
                $post_errors->addError('no_name');
            }
            if (Util::strlen($_POST['guestname']) > 25) {
                $post_errors->addError('long_name');
            }
            if (empty($modSettings['guest_post_no_email'])) {
                // Only check if they changed it!
                if (!isset($msgInfo) || $msgInfo['poster_email'] != $_POST['email']) {
                    require_once SUBSDIR . '/DataValidator.class.php';
                    if (!allowedTo('moderate_forum') && !Data_Validator::is_valid($_POST, array('email' => 'valid_email|required'), array('email' => 'trim'))) {
                        empty($_POST['email']) ? $post_errors->addError('no_email') : $post_errors->addError('bad_email');
                    }
                }
                // Now make sure this email address is not banned from posting.
                isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title']));
            }
            // In case they are making multiple posts this visit, help them along by storing their name.
            if (!$post_errors->hasErrors()) {
                $_SESSION['guest_name'] = $_POST['guestname'];
                $_SESSION['guest_email'] = $_POST['email'];
            }
        }
        // Check the subject and message.
        if (!isset($_POST['subject']) || Util::htmltrim(Util::htmlspecialchars($_POST['subject'])) === '') {
            $post_errors->addError('no_subject');
        }
        if (!isset($_POST['message']) || Util::htmltrim(Util::htmlspecialchars($_POST['message'], ENT_QUOTES)) === '') {
            $post_errors->addError('no_message');
        } elseif (!empty($modSettings['max_messageLength']) && Util::strlen($_POST['message']) > $modSettings['max_messageLength']) {
            $post_errors->addError(array('long_message', array($modSettings['max_messageLength'])));
        } else {
            // Prepare the message a bit for some additional testing.
            $_POST['message'] = Util::htmlspecialchars($_POST['message'], ENT_QUOTES);
            // Preparse code. (Zef)
            if ($user_info['is_guest']) {
                $user_info['name'] = $_POST['guestname'];
            }
            preparsecode($_POST['message']);
            // Let's see if there's still some content left without the tags.
            if (Util::htmltrim(strip_tags(parse_bbc($_POST['message'], false), '<img>')) === '' && (!allowedTo('admin_forum') || strpos($_POST['message'], '[html]') === false)) {
                $post_errors->addError('no_message');
            }
        }
        if (isset($_POST['calendar']) && !isset($_REQUEST['deleteevent']) && Util::htmltrim($_POST['evtitle']) === '') {
            $post_errors->addError('no_event');
        }
        // Validate the poll...
        if (isset($_REQUEST['poll']) && !empty($modSettings['pollMode'])) {
            if (!empty($topic) && !isset($_REQUEST['msg'])) {
                fatal_lang_error('no_access', false);
            }
            // This is a new topic... so it's a new poll.
            if (empty($topic)) {
                isAllowedTo('poll_post');
            } elseif ($user_info['id'] == $topic_info['id_member_started'] && !allowedTo('poll_add_any')) {
                isAllowedTo('poll_add_own');
            } else {
                isAllowedTo('poll_add_any');
            }
            if (!isset($_POST['question']) || trim($_POST['question']) == '') {
                $post_errors->addError('no_question');
            }
            $_POST['options'] = empty($_POST['options']) ? array() : htmltrim__recursive($_POST['options']);
            // Get rid of empty ones.
            foreach ($_POST['options'] as $k => $option) {
                if ($option == '') {
                    unset($_POST['options'][$k], $_POST['options'][$k]);
                }
            }
            // What are you going to vote between with one choice?!?
            if (count($_POST['options']) < 2) {
                $post_errors->addError('poll_few');
            } elseif (count($_POST['options']) > 256) {
                $post_errors->addError('poll_many');
            }
        }
        if ($posterIsGuest) {
            // If user is a guest, make sure the chosen name isn't taken.
            require_once SUBSDIR . '/Members.subs.php';
            if (isReservedName($_POST['guestname'], 0, true, false) && (!isset($msgInfo['poster_name']) || $_POST['guestname'] != $msgInfo['poster_name'])) {
                $post_errors->addError('bad_name');
            }
        } elseif (!isset($_REQUEST['msg'])) {
            $_POST['guestname'] = $user_info['username'];
            $_POST['email'] = $user_info['email'];
        }
        // Posting somewhere else? Are we sure you can?
        if (!empty($_REQUEST['post_in_board'])) {
            $new_board = (int) $_REQUEST['post_in_board'];
            if (!allowedTo('post_new', $new_board)) {
                $post_in_board = boardInfo($new_board);
                if (!empty($post_in_board)) {
                    $post_errors->addError(array('post_new_board', array($post_in_board['name'])));
                } else {
                    $post_errors->addError('post_new');
                }
            }
        }
        // Any mistakes?
        if ($post_errors->hasErrors() || $attach_errors->hasErrors()) {
            addInlineJavascript('
				$(document).ready(function () {
					$("html,body").scrollTop($(\'.category_header:visible:first\').offset().top);
				});');
            return $this->action_post();
        }
        // Make sure the user isn't spamming the board.
        if (!isset($_REQUEST['msg'])) {
            spamProtection('post');
        }
        // At about this point, we're posting and that's that.
        ignore_user_abort(true);
        @set_time_limit(300);
        // Add special html entities to the subject, name, and email.
        $_POST['subject'] = strtr(Util::htmlspecialchars($_POST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
        $_POST['guestname'] = htmlspecialchars($_POST['guestname'], ENT_COMPAT, 'UTF-8');
        $_POST['email'] = htmlspecialchars($_POST['email'], ENT_COMPAT, 'UTF-8');
        // At this point, we want to make sure the subject isn't too long.
        if (Util::strlen($_POST['subject']) > 100) {
            $_POST['subject'] = Util::substr($_POST['subject'], 0, 100);
        }
        if (!empty($modSettings['mentions_enabled']) && !empty($_REQUEST['uid'])) {
            $query_params = array();
            $query_params['member_ids'] = array_unique(array_map('intval', $_REQUEST['uid']));
            require_once SUBSDIR . '/Members.subs.php';
            $mentioned_members = membersBy('member_ids', $query_params, true);
            $replacements = 0;
            $actually_mentioned = array();
            foreach ($mentioned_members as $member) {
                $_POST['message'] = str_replace('@' . $member['real_name'], '[member=' . $member['id_member'] . ']' . $member['real_name'] . '[/member]', $_POST['message'], $replacements);
                if ($replacements > 0) {
                    $actually_mentioned[] = $member['id_member'];
                }
            }
        }
        // Make the poll...
        if (isset($_REQUEST['poll'])) {
            // Make sure that the user has not entered a ridiculous number of options..
            if (empty($_POST['poll_max_votes']) || $_POST['poll_max_votes'] <= 0) {
                $_POST['poll_max_votes'] = 1;
            } elseif ($_POST['poll_max_votes'] > count($_POST['options'])) {
                $_POST['poll_max_votes'] = count($_POST['options']);
            } else {
                $_POST['poll_max_votes'] = (int) $_POST['poll_max_votes'];
            }
            $_POST['poll_expire'] = (int) $_POST['poll_expire'];
            $_POST['poll_expire'] = $_POST['poll_expire'] > 9999 ? 9999 : ($_POST['poll_expire'] < 0 ? 0 : $_POST['poll_expire']);
            // Just set it to zero if it's not there..
            if (!isset($_POST['poll_hide'])) {
                $_POST['poll_hide'] = 0;
            } else {
                $_POST['poll_hide'] = (int) $_POST['poll_hide'];
            }
            $_POST['poll_change_vote'] = isset($_POST['poll_change_vote']) ? 1 : 0;
            $_POST['poll_guest_vote'] = isset($_POST['poll_guest_vote']) ? 1 : 0;
            // Make sure guests are actually allowed to vote generally.
            if ($_POST['poll_guest_vote']) {
                require_once SUBSDIR . '/Members.subs.php';
                $allowedVoteGroups = groupsAllowedTo('poll_vote', $board);
                if (!in_array(-1, $allowedVoteGroups['allowed'])) {
                    $_POST['poll_guest_vote'] = 0;
                }
            }
            // If the user tries to set the poll too far in advance, don't let them.
            if (!empty($_POST['poll_expire']) && $_POST['poll_expire'] < 1) {
                fatal_lang_error('poll_range_error', false);
            } elseif (empty($_POST['poll_expire']) && $_POST['poll_hide'] == 2) {
                $_POST['poll_hide'] = 1;
            }
            // Clean up the question and answers.
            $_POST['question'] = htmlspecialchars($_POST['question'], ENT_COMPAT, 'UTF-8');
            $_POST['question'] = Util::substr($_POST['question'], 0, 255);
            $_POST['question'] = preg_replace('~&amp;#(\\d{4,5}|[2-9]\\d{2,4}|1[2-9]\\d);~', '&#$1;', $_POST['question']);
            $_POST['options'] = htmlspecialchars__recursive($_POST['options']);
            // Finally, make the poll.
            require_once SUBSDIR . '/Poll.subs.php';
            $id_poll = createPoll($_POST['question'], $user_info['id'], $_POST['guestname'], $_POST['poll_max_votes'], $_POST['poll_hide'], $_POST['poll_expire'], $_POST['poll_change_vote'], $_POST['poll_guest_vote'], $_POST['options']);
        } else {
            $id_poll = 0;
        }
        // ...or attach a new file...
        if (empty($ignore_temp) && $context['attachments']['can']['post'] && !empty($_SESSION['temp_attachments']) && empty($_POST['from_qr'])) {
            $attachIDs = array();
            foreach ($_SESSION['temp_attachments'] as $attachID => $attachment) {
                if ($attachID != 'initial_error' && strpos($attachID, 'post_tmp_' . $user_info['id']) === false) {
                    continue;
                }
                // If there was an initial error just show that message.
                if ($attachID == 'initial_error') {
                    unset($_SESSION['temp_attachments']);
                    break;
                }
                // No errors, then try to create the attachment
                if (empty($attachment['errors'])) {
                    // Load the attachmentOptions array with the data needed to create an attachment
                    $attachmentOptions = array('post' => isset($_REQUEST['msg']) ? $_REQUEST['msg'] : 0, 'poster' => $user_info['id'], 'name' => $attachment['name'], 'tmp_name' => $attachment['tmp_name'], 'size' => isset($attachment['size']) ? $attachment['size'] : 0, 'mime_type' => isset($attachment['type']) ? $attachment['type'] : '', 'id_folder' => isset($attachment['id_folder']) ? $attachment['id_folder'] : 0, 'approved' => !$modSettings['postmod_active'] || allowedTo('post_attachment'), 'errors' => array());
                    if (createAttachment($attachmentOptions)) {
                        $attachIDs[] = $attachmentOptions['id'];
                        if (!empty($attachmentOptions['thumb'])) {
                            $attachIDs[] = $attachmentOptions['thumb'];
                        }
                    }
                } else {
                    @unlink($attachment['tmp_name']);
                }
            }
            unset($_SESSION['temp_attachments']);
        }
        // Creating a new topic?
        $newTopic = empty($_REQUEST['msg']) && empty($topic);
        $_POST['icon'] = !empty($attachIDs) && $_POST['icon'] == 'xx' ? 'clip' : $_POST['icon'];
        // Collect all parameters for the creation or modification of a post.
        $msgOptions = array('id' => empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg'], 'subject' => $_POST['subject'], 'body' => $_POST['message'], 'icon' => preg_replace('~[\\./\\\\*:"\'<>]~', '', $_POST['icon']), 'smileys_enabled' => !isset($_POST['ns']), 'attachments' => empty($attachIDs) ? array() : $attachIDs, 'approved' => $becomesApproved);
        $topicOptions = array('id' => empty($topic) ? 0 : $topic, 'board' => $board, 'poll' => isset($_REQUEST['poll']) ? $id_poll : null, 'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null, 'sticky_mode' => isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics']) ? (int) $_POST['sticky'] : null, 'mark_as_read' => true, 'is_approved' => !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']));
        $posterOptions = array('id' => $user_info['id'], 'name' => $_POST['guestname'], 'email' => $_POST['email'], 'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count']);
        // This is an already existing message. Edit it.
        if (!empty($_REQUEST['msg'])) {
            // Have admins allowed people to hide their screwups?
            if (time() - $msgInfo['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $msgInfo['id_member']) {
                $msgOptions['modify_time'] = time();
                $msgOptions['modify_name'] = $user_info['name'];
            }
            // This will save some time...
            if (empty($approve_has_changed)) {
                unset($msgOptions['approved']);
            }
            modifyPost($msgOptions, $topicOptions, $posterOptions);
        } else {
            if (!empty($modSettings['enableFollowup']) && !empty($_REQUEST['followup'])) {
                $original_post = (int) $_REQUEST['followup'];
            }
            // We also have to fake the board:
            // if it's valid and it's not the current, let's forget about the "current" and load the new one
            if (!empty($new_board) && $board !== $new_board) {
                $board = $new_board;
                loadBoard();
                // Some details changed
                $topicOptions['board'] = $board;
                $topicOptions['is_approved'] = !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']);
                $posterOptions['update_post_count'] = !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count'];
            }
            createPost($msgOptions, $topicOptions, $posterOptions);
            if (isset($topicOptions['id'])) {
                $topic = $topicOptions['id'];
            }
            if (!empty($modSettings['enableFollowup'])) {
                require_once SUBSDIR . '/FollowUps.subs.php';
                require_once SUBSDIR . '/Messages.subs.php';
                // Time to update the original message with a pointer to the new one
                if (!empty($original_post) && canAccessMessage($original_post)) {
                    linkMessages($original_post, $topic);
                }
            }
        }
        // If we had a draft for this, its time to remove it since it was just posted
        if (!empty($modSettings['drafts_enabled']) && !empty($_POST['id_draft'])) {
            deleteDrafts($_POST['id_draft'], $user_info['id']);
        }
        // Editing or posting an event?
        if (isset($_POST['calendar']) && (!isset($_REQUEST['eventid']) || $_REQUEST['eventid'] == -1)) {
            require_once SUBSDIR . '/Calendar.subs.php';
            // Make sure they can link an event to this post.
            canLinkEvent();
            // Insert the event.
            $eventOptions = array('id_board' => $board, 'id_topic' => $topic, 'title' => $_POST['evtitle'], 'member' => $user_info['id'], 'start_date' => sprintf('%04d-%02d-%02d', $_POST['year'], $_POST['month'], $_POST['day']), 'span' => isset($_POST['span']) && $_POST['span'] > 0 ? min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1) : 0);
            insertEvent($eventOptions);
        } elseif (isset($_POST['calendar'])) {
            $_REQUEST['eventid'] = (int) $_REQUEST['eventid'];
            // Validate the post...
            require_once SUBSDIR . '/Calendar.subs.php';
            validateEventPost();
            // If you're not allowed to edit any events, you have to be the poster.
            if (!allowedTo('calendar_edit_any')) {
                $event_poster = getEventPoster($_REQUEST['eventid']);
                // Silly hacker, Trix are for kids. ...probably trademarked somewhere, this is FAIR USE! (parody...)
                isAllowedTo('calendar_edit_' . ($event_poster == $user_info['id'] ? 'own' : 'any'));
            }
            // Delete it?
            if (isset($_REQUEST['deleteevent'])) {
                removeEvent($_REQUEST['eventid']);
            } else {
                $span = !empty($modSettings['cal_allowspan']) && !empty($_REQUEST['span']) ? min((int) $modSettings['cal_maxspan'], (int) $_REQUEST['span'] - 1) : 0;
                $start_time = mktime(0, 0, 0, (int) $_REQUEST['month'], (int) $_REQUEST['day'], (int) $_REQUEST['year']);
                $eventOptions = array('start_date' => strftime('%Y-%m-%d', $start_time), 'end_date' => strftime('%Y-%m-%d', $start_time + $span * 86400), 'title' => $_REQUEST['evtitle']);
                modifyEvent($_REQUEST['eventid'], $eventOptions);
            }
        }
        // Marking boards as read.
        // (You just posted and they will be unread.)
        if (!$user_info['is_guest']) {
            $board_list = !empty($board_info['parent_boards']) ? array_keys($board_info['parent_boards']) : array();
            // Returning to the topic?
            if (!empty($_REQUEST['goback'])) {
                $board_list[] = $board;
            }
            if (!empty($board_list)) {
                markBoardsRead($board_list, false, false);
            }
        }
        // Turn notification on or off.
        if (!empty($_POST['notify']) && allowedTo('mark_any_notify')) {
            setTopicNotification($user_info['id'], $topic, true);
        } elseif (!$newTopic) {
            setTopicNotification($user_info['id'], $topic, false);
        }
        // Log an act of moderation - modifying.
        if (!empty($moderationAction)) {
            logAction('modify', array('topic' => $topic, 'message' => (int) $_REQUEST['msg'], 'member' => $msgInfo['id_member'], 'board' => $board));
        }
        if (isset($_POST['lock']) && $_POST['lock'] != 2) {
            logAction(empty($_POST['lock']) ? 'unlock' : 'lock', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
        }
        if (isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics'])) {
            logAction(empty($_POST['sticky']) ? 'unsticky' : 'sticky', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board']));
        }
        // Notify any members who have notification turned on for this topic/board - only do this if it's going to be approved(!)
        if ($becomesApproved) {
            require_once SUBSDIR . '/Notification.subs.php';
            if ($newTopic) {
                $notifyData = array('body' => $_POST['message'], 'subject' => $_POST['subject'], 'name' => $user_info['name'], 'poster' => $user_info['id'], 'msg' => $msgOptions['id'], 'board' => $board, 'topic' => $topic, 'signature' => isset($user_settings['signature']) ? $user_settings['signature'] : '');
                sendBoardNotifications($notifyData);
            } elseif (empty($_REQUEST['msg'])) {
                // Only send it to everyone if the topic is approved, otherwise just to the topic starter if they want it.
                if ($topic_info['approved']) {
                    sendNotifications($topic, 'reply');
                } else {
                    sendNotifications($topic, 'reply', array(), $topic_info['id_member_started']);
                }
            }
        }
        if (!empty($modSettings['mentions_enabled']) && !empty($actually_mentioned)) {
            require_once CONTROLLERDIR . '/Mentions.controller.php';
            $mentions = new Mentions_Controller();
            $mentions->setData(array('id_member' => $actually_mentioned, 'type' => 'men', 'id_msg' => $msgOptions['id'], 'status' => $becomesApproved ? 'new' : 'unapproved'));
            $mentions->action_add();
        }
        if ($board_info['num_topics'] == 0) {
            cache_put_data('board-' . $board, null, 120);
        }
        if (!empty($_POST['announce_topic'])) {
            redirectexit('action=announce;sa=selectgroup;topic=' . $topic . (!empty($_POST['move']) && allowedTo('move_any') ? ';move' : '') . (empty($_REQUEST['goback']) ? '' : ';goback'));
        }
        if (!empty($_POST['move']) && allowedTo('move_any')) {
            redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback'));
        }
        // Return to post if the mod is on.
        if (isset($_REQUEST['msg']) && !empty($_REQUEST['goback'])) {
            redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg'], isBrowser('ie'));
        } elseif (!empty($_REQUEST['goback'])) {
            redirectexit('topic=' . $topic . '.new#new', isBrowser('ie'));
        } else {
            redirectexit('board=' . $board . '.0');
        }
    }
 /**
  * Send a personal message.
  */
 public function action_send2()
 {
     global $txt, $context, $user_info, $modSettings;
     // All the helpers we need
     require_once SUBSDIR . '/Auth.subs.php';
     require_once SUBSDIR . '/Post.subs.php';
     // PM Drafts enabled and needed?
     if ($context['drafts_pm_save'] && (isset($_POST['save_draft']) || isset($_POST['id_pm_draft']))) {
         require_once SUBSDIR . '/Drafts.subs.php';
     }
     loadLanguage('PersonalMessage', '', false);
     // Extract out the spam settings - it saves database space!
     list($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
     // Initialize the errors we're about to make.
     $post_errors = Error_Context::context('pm', 1);
     // Check whether we've gone over the limit of messages we can send per hour - fatal error if fails!
     if (!empty($modSettings['pm_posts_per_hour']) && !allowedTo(array('admin_forum', 'moderate_forum', 'send_mail')) && $user_info['mod_cache']['bq'] == '0=1' && $user_info['mod_cache']['gq'] == '0=1') {
         // How many have they sent this last hour?
         $pmCount = pmCount($user_info['id'], 3600);
         if (!empty($pmCount) && $pmCount >= $modSettings['pm_posts_per_hour']) {
             if (!isset($_REQUEST['xml'])) {
                 fatal_lang_error('pm_too_many_per_hour', true, array($modSettings['pm_posts_per_hour']));
             } else {
                 $post_errors->addError('pm_too_many_per_hour');
             }
         }
     }
     // If your session timed out, show an error, but do allow to re-submit.
     if (!isset($_REQUEST['xml']) && checkSession('post', '', false) != '') {
         $post_errors->addError('session_timeout');
     }
     $_REQUEST['subject'] = isset($_REQUEST['subject']) ? strtr(Util::htmltrim($_POST['subject']), array("\r" => '', "\n" => '', "\t" => '')) : '';
     $_REQUEST['to'] = empty($_POST['to']) ? empty($_GET['to']) ? '' : $_GET['to'] : $_POST['to'];
     $_REQUEST['bcc'] = empty($_POST['bcc']) ? empty($_GET['bcc']) ? '' : $_GET['bcc'] : $_POST['bcc'];
     // Route the input from the 'u' parameter to the 'to'-list.
     if (!empty($_POST['u'])) {
         $_POST['recipient_to'] = explode(',', $_POST['u']);
     }
     // Construct the list of recipients.
     $recipientList = array();
     $namedRecipientList = array();
     $namesNotFound = array();
     foreach (array('to', 'bcc') as $recipientType) {
         // First, let's see if there's user ID's given.
         $recipientList[$recipientType] = array();
         if (!empty($_POST['recipient_' . $recipientType]) && is_array($_POST['recipient_' . $recipientType])) {
             foreach ($_POST['recipient_' . $recipientType] as $recipient) {
                 $recipientList[$recipientType][] = (int) $recipient;
             }
         }
         // Are there also literal names set?
         if (!empty($_REQUEST[$recipientType])) {
             // We're going to take out the "s anyway ;).
             $recipientString = strtr($_REQUEST[$recipientType], array('\\"' => '"'));
             preg_match_all('~"([^"]+)"~', $recipientString, $matches);
             $namedRecipientList[$recipientType] = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $recipientString))));
             // Clean any literal names entered
             foreach ($namedRecipientList[$recipientType] as $index => $recipient) {
                 if (strlen(trim($recipient)) > 0) {
                     $namedRecipientList[$recipientType][$index] = Util::htmlspecialchars(Util::strtolower(trim($recipient)));
                 } else {
                     unset($namedRecipientList[$recipientType][$index]);
                 }
             }
             // Now see if we can resolove the entered name to an actual user
             if (!empty($namedRecipientList[$recipientType])) {
                 $foundMembers = findMembers($namedRecipientList[$recipientType]);
                 // Assume all are not found, until proven otherwise.
                 $namesNotFound[$recipientType] = $namedRecipientList[$recipientType];
                 // Make sure we only have each member listed once, incase they did not use the select list
                 foreach ($foundMembers as $member) {
                     $testNames = array(Util::strtolower($member['username']), Util::strtolower($member['name']), Util::strtolower($member['email']));
                     if (count(array_intersect($testNames, $namedRecipientList[$recipientType])) !== 0) {
                         $recipientList[$recipientType][] = $member['id'];
                         // Get rid of this username, since we found it.
                         $namesNotFound[$recipientType] = array_diff($namesNotFound[$recipientType], $testNames);
                     }
                 }
             }
         }
         // Selected a recipient to be deleted? Remove them now.
         if (!empty($_POST['delete_recipient'])) {
             $recipientList[$recipientType] = array_diff($recipientList[$recipientType], array((int) $_POST['delete_recipient']));
         }
         // Make sure we don't include the same name twice
         $recipientList[$recipientType] = array_unique($recipientList[$recipientType]);
     }
     // Are we changing the recipients some how?
     $is_recipient_change = !empty($_POST['delete_recipient']) || !empty($_POST['to_submit']) || !empty($_POST['bcc_submit']);
     // Check if there's at least one recipient.
     if (empty($recipientList['to']) && empty($recipientList['bcc'])) {
         $post_errors->addError('no_to');
     }
     // Make sure that we remove the members who did get it from the screen.
     if (!$is_recipient_change) {
         foreach (array_keys($recipientList) as $recipientType) {
             if (!empty($namesNotFound[$recipientType])) {
                 $post_errors->addError('bad_' . $recipientType);
                 // Since we already have a post error, remove the previous one.
                 $post_errors->removeError('no_to');
                 foreach ($namesNotFound[$recipientType] as $name) {
                     $context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name);
                 }
             }
         }
     }
     // Did they make any mistakes like no subject or message?
     if ($_REQUEST['subject'] == '') {
         $post_errors->addError('no_subject');
     }
     if (!isset($_REQUEST['message']) || $_REQUEST['message'] == '') {
         $post_errors->addError('no_message');
     } elseif (!empty($modSettings['max_messageLength']) && Util::strlen($_REQUEST['message']) > $modSettings['max_messageLength']) {
         $post_errors->addError('long_message');
     } else {
         // Preparse the message.
         $message = $_REQUEST['message'];
         preparsecode($message);
         // Make sure there's still some content left without the tags.
         if (Util::htmltrim(strip_tags(parse_bbc(Util::htmlspecialchars($message, ENT_QUOTES), false), '<img>')) === '' && (!allowedTo('admin_forum') || strpos($message, '[html]') === false)) {
             $post_errors->addError('no_message');
         }
     }
     // Wrong verification code?
     if (!$user_info['is_admin'] && !isset($_REQUEST['xml']) && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification']) {
         require_once SUBSDIR . '/VerificationControls.class.php';
         $verificationOptions = array('id' => 'pm');
         $context['require_verification'] = create_control_verification($verificationOptions, true);
         if (is_array($context['require_verification'])) {
             foreach ($context['require_verification'] as $error) {
                 $post_errors->addError($error);
             }
         }
     }
     // If they made any errors, give them a chance to make amends.
     if ($post_errors->hasErrors() && !$is_recipient_change && !isset($_REQUEST['preview']) && !isset($_REQUEST['xml'])) {
         return messagePostError($namedRecipientList, $recipientList);
     }
     // Want to take a second glance before you send?
     if (isset($_REQUEST['preview'])) {
         // Set everything up to be displayed.
         $context['preview_subject'] = Util::htmlspecialchars($_REQUEST['subject']);
         $context['preview_message'] = Util::htmlspecialchars($_REQUEST['message'], ENT_QUOTES, 'UTF-8', true);
         preparsecode($context['preview_message'], true);
         // Parse out the BBC if it is enabled.
         $context['preview_message'] = parse_bbc($context['preview_message']);
         // Censor, as always.
         censorText($context['preview_subject']);
         censorText($context['preview_message']);
         // Set a descriptive title.
         $context['page_title'] = $txt['preview'] . ' - ' . $context['preview_subject'];
         // Pretend they messed up but don't ignore if they really did :P.
         return messagePostError($namedRecipientList, $recipientList);
     } elseif ($is_recipient_change) {
         // Maybe we couldn't find one?
         foreach ($namesNotFound as $recipientType => $names) {
             $post_errors->addError('bad_' . $recipientType);
             foreach ($names as $name) {
                 $context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name);
             }
         }
         return messagePostError($namedRecipientList, $recipientList);
     }
     // Want to save this as a draft and think about it some more?
     if ($context['drafts_pm_save'] && isset($_POST['save_draft'])) {
         savePMDraft($recipientList);
         return messagePostError($namedRecipientList, $recipientList);
     } elseif (!empty($modSettings['max_pm_recipients']) && count($recipientList['to']) + count($recipientList['bcc']) > $modSettings['max_pm_recipients'] && !allowedTo(array('moderate_forum', 'send_mail', 'admin_forum'))) {
         $context['send_log'] = array('sent' => array(), 'failed' => array(sprintf($txt['pm_too_many_recipients'], $modSettings['max_pm_recipients'])));
         return messagePostError($namedRecipientList, $recipientList);
     }
     // Protect from message spamming.
     spamProtection('pm');
     // Prevent double submission of this form.
     checkSubmitOnce('check');
     // Finally do the actual sending of the PM.
     if (!empty($recipientList['to']) || !empty($recipientList['bcc'])) {
         $context['send_log'] = sendpm($recipientList, $_REQUEST['subject'], $_REQUEST['message'], true, null, !empty($_REQUEST['pm_head']) ? (int) $_REQUEST['pm_head'] : 0);
     } else {
         $context['send_log'] = array('sent' => array(), 'failed' => array());
     }
     // Mark the message as "replied to".
     if (!empty($context['send_log']['sent']) && !empty($_REQUEST['replied_to']) && isset($_REQUEST['f']) && $_REQUEST['f'] == 'inbox') {
         require_once SUBSDIR . '/PersonalMessage.subs.php';
         setPMRepliedStatus($user_info['id'], (int) $_REQUEST['replied_to']);
     }
     // If one or more of the recipients were invalid, go back to the post screen with the failed usernames.
     if (!empty($context['send_log']['failed'])) {
         return messagePostError($namesNotFound, array('to' => array_intersect($recipientList['to'], $context['send_log']['failed']), 'bcc' => array_intersect($recipientList['bcc'], $context['send_log']['failed'])));
     }
     // Message sent successfully?
     if (!empty($context['send_log']) && empty($context['send_log']['failed'])) {
         $context['current_label_redirect'] = $context['current_label_redirect'] . ';done=sent';
         // If we had a PM draft for this one, then its time to remove it since it was just sent
         if ($context['drafts_pm_save'] && !empty($_POST['id_pm_draft'])) {
             deleteDrafts($_POST['id_pm_draft'], $user_info['id']);
         }
     }
     // Go back to the where they sent from, if possible...
     redirectexit($context['current_label_redirect']);
 }
Beispiel #14
0
function doTPpage()
{
    global $context, $scripturl, $txt, $modSettings, $boarddir, $sourcedir, $smcFunc;
    $now = time();
    // Set the avatar height/width
    $avatar_width = '';
    $avatar_height = '';
    if ($modSettings['avatar_action_too_large'] == 'option_html_resize' || $modSettings['avatar_action_too_large'] == 'option_js_resize') {
        $avatar_width = !empty($modSettings['avatar_max_width_external']) ? ' width="' . $modSettings['avatar_max_width_external'] . '"' : '';
        $avatar_height = !empty($modSettings['avatar_max_height_external']) ? ' height="' . $modSettings['avatar_max_height_external'] . '"' : '';
    }
    // check validity and fetch it
    if (!empty($_GET['page'])) {
        $page = tp_sanitize($_GET['page']);
        $pag = is_numeric($page) ? 'art.id = {int:page}' : 'art.shortname = {string:page}';
        if (allowedTo('tp_articles')) {
            $request = $smcFunc['db_query']('', '
				SELECT art.*, art.author_id as authorID, art.id_theme as ID_THEME, var.value1,
					var.value2, var.value3, var.value4, var.value5, var.value7, var.value8,
					art.type as rendertype, IFNULL(mem.real_name,art.author) as realName, mem.avatar,
					mem.posts, mem.date_registered as dateRegistered,mem.last_login as lastLogin,
					IFNULL(a.id_attach, 0) AS ID_ATTACH, a.filename, a.attachment_type as attachmentType, var.value9
				FROM {db_prefix}tp_articles as art
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = art.author_id)
				LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = art.author_id AND a.attachment_type != 3)
				LEFT JOIN {db_prefix}tp_variables as var ON (var.id= art.category)
				WHERE ' . $pag . '
				LIMIT 1', array('page' => is_numeric($page) ? (int) $page : $page));
        } else {
            $request = $smcFunc['db_query']('', '
				SELECT art.*, art.author_id as authorID, art.id_theme as ID_THEME, var.value1, var.value2,
					var.value3,var.value4, var.value5,var.value7,var.value8, art.type as rendertype,
					IFNULL(mem.real_name,art.author) as realName, mem.avatar, mem.posts, mem.date_registered as dateRegistered, mem.last_login as lastLogin,
					IFNULL(a.id_attach, 0) AS ID_ATTACH, a.filename, a.attachment_type as attachmentType, var.value9
				FROM {db_prefix}tp_articles as art
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = art.author_id)
				LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = art.author_id AND a.attachment_type != 3)
				LEFT JOIN {db_prefix}tp_variables as var ON (var.id = art.category)
				WHERE ' . $pag . '
				AND ((art.pub_start = 0 AND art.pub_end = 0)
				OR (art.pub_start != 0 AND art.pub_start < ' . $now . ' AND art.pub_end = 0)
				OR (art.pub_start = 0 AND art.pub_end != 0 AND art.pub_end > ' . $now . ')
				OR (art.pub_start != 0 AND art.pub_end != 0 AND art.pub_end > ' . $now . ' AND art.pub_start < ' . $now . '))
				LIMIT 1', array('page' => is_numeric($page) ? (int) $page : $page));
        }
        if ($smcFunc['db_num_rows']($request) > 0) {
            $article = $smcFunc['db_fetch_assoc']($request);
            $valid = true;
            $smcFunc['db_free_result']($request);
            // if its not approved, say so.
            if ($article['approved'] == 0) {
                TP_error($txt['tp-notapproved']);
                $shown = true;
                if (!allowedTo('tp_articles')) {
                    $valid = false;
                }
            }
            // likewise for off.
            if ($article['off'] == 1) {
                if (!isset($shown)) {
                    TP_error($txt['tp-noton']);
                }
                $shown = true;
                if (!allowedTo('tp_articles')) {
                    $valid = false;
                }
            }
            // and for no category
            if ($article['category'] < 1 || $article['category'] > 9999) {
                if (!isset($shown)) {
                    TP_error($txt['tp-nocategory']);
                }
                $shown = true;
                if (!allowedTo('tp_articles')) {
                    $valid = false;
                }
            }
            if (get_perm($article['value3']) && $valid) {
                // compability towards old articles
                if (empty($article['type'])) {
                    $article['type'] = $article['rendertype'] = 'html';
                }
                // shortname title
                $article['shortname'] = un_htmlspecialchars($article['shortname']);
                // Add ratings together
                $article['rating'] = array_sum(explode(',', $article['rating']));
                // allowed and all is well, go on with it.
                $context['TPortal']['article'] = $article;
                $context['TPortal']['article']['avatar'] = $article['avatar'] == '' ? $article['ID_ATTACH'] > 0 ? '<img src="' . (empty($article['attachmentType']) ? $scripturl . '?action=dlattach;attach=' . $article['ID_ATTACH'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $article['filename']) . '" alt="&nbsp;"  />' : '' : (stristr($article['avatar'], 'http://') ? '<img src="' . $article['avatar'] . '" alt="&nbsp;" />' : '<img src="' . $modSettings['avatar_url'] . '/' . $smcFunc['htmlspecialchars']($article['avatar'], ENT_QUOTES) . '" alt="&nbsp;" />');
                // update views
                $smcFunc['db_query']('', '
					UPDATE {db_prefix}tp_articles
					SET views = views + 1
					WHERE ' . (is_numeric($page) ? 'id = {int:page}' : 'shortname = {string:page}') . ' LIMIT 1', array('page' => $page));
                // fetch and update last access by member(to log which comment is new)
                $now = time();
                $request = $smcFunc['db_query']('', '
					SELECT item FROM {db_prefix}tp_data
						WHERE id_member = {int:id_mem}
						AND type = {int:type}
						AND value = {int:val} LIMIT 1', array('id_mem' => $context['user']['id'], 'type' => 1, 'val' => $article['id']));
                if ($smcFunc['db_num_rows']($request) > 0) {
                    $row = $smcFunc['db_fetch_row']($request);
                    $last = $row[0];
                    $smcFunc['db_free_result']($request);
                    $smcFunc['db_query']('', '
						UPDATE {db_prefix}tp_data
						SET item = {int:item}
						WHERE id_member = {int:id_mem}
						AND type = {int:type}
						AND value = {int:val}', array('item' => $now, 'id_mem' => $context['user']['id'], 'type' => 1, 'val' => $article['id']));
                } else {
                    $last = $now;
                    $smcFunc['db_insert']('INSERT', '{db_prefix}tp_data', array('type' => 'int', 'id_member' => 'int', 'value' => 'int', 'item' => 'int'), array(1, $context['user']['id'], $article['id'], $now), array('id'));
                }
                require_once $sourcedir . '/TPcommon.php';
                // ok, how many articles have this member posted?
                $request = $smcFunc['db_query']('', '
					SELECT id FROM {db_prefix}tp_articles
					WHERE author_id = {int:author}
					AND off = 0', array('author' => $context['TPortal']['article']['authorID']));
                $context['TPortal']['article']['countarticles'] = $smcFunc['db_num_rows']($request);
                $smcFunc['db_free_result']($request);
                // We'll use this in the template to allow comment box
                if (allowedTo('tp_artcomment')) {
                    $context['TPortal']['can_artcomment'] = true;
                }
                // fetch any comments
                $request = $smcFunc['db_query']('', '
					SELECT var.* , IFNULL(mem.real_name,0) as realName,mem.avatar,
						IFNULL(a.id_attach, 0) AS ID_ATTACH, a.filename, a.attachment_type as attachmentType
					FROM {db_prefix}tp_variables AS var
					LEFT JOIN {db_prefix}members as mem ON (var.value3 = mem.id_member)
					LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member)
					WHERE var.type = {string:type}
					AND var.value5 = {int:val5}
					ORDER BY var.value4 ASC', array('type' => 'article_comment', 'val5' => $article['id']));
                $ccount = 0;
                $newcount = 0;
                if ($smcFunc['db_num_rows']($request) > 0) {
                    $context['TPortal']['article']['comment_posts'] = array();
                    while ($row = $smcFunc['db_fetch_assoc']($request)) {
                        $context['TPortal']['article']['comment_posts'][] = array('id' => $row['id'], 'subject' => '<a href="' . $scripturl . '?page=' . $context['TPortal']['article']['id'] . '#comment' . $row['id'] . '">' . $row['value1'] . '</a>', 'text' => parse_bbc($row['value2']), 'timestamp' => $row['value4'], 'date' => timeformat($row['value4']), 'posterID' => $row['value3'], 'poster' => $row['realName'], 'is_new' => $row['value4'] > $last ? true : false, 'avatar' => array('name' => &$row['avatar'], 'image' => $row['avatar'] == '' ? $row['ID_ATTACH'] > 0 ? '<img src="' . (empty($row['attachmentType']) ? $scripturl . '?action=tpmod;sa=tpattach;attach=' . $row['ID_ATTACH'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $row['filename']) . '" alt="" class="avatar" border="0" />' : '' : (stristr($row['avatar'], 'http://') ? '<img src="' . $row['avatar'] . '"' . $avatar_width . $avatar_height . ' alt="" class="avatar" border="0" />' : '<img src="' . $modSettings['avatar_url'] . '/' . $smcFunc['htmlspecialchars']($row['avatar'], ENT_QUOTES) . '" alt="" class="avatar" border="0" />'), 'href' => $row['avatar'] == '' ? $row['ID_ATTACH'] > 0 ? empty($row['attachmentType']) ? $scripturl . '?action=tpmod;sa=tpattach;attach=' . $row['ID_ATTACH'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $row['filename'] : '' : (stristr($row['avatar'], 'http://') ? $row['avatar'] : $modSettings['avatar_url'] . '/' . $row['avatar']), 'url' => $row['avatar'] == '' ? '' : (stristr($row['avatar'], 'http://') ? $row['avatar'] : $modSettings['avatar_url'] . '/' . $row['avatar'])));
                        $ccount++;
                        if ($row['value4'] > $last) {
                            $newcount++;
                        }
                    }
                    $context['TPortal']['article_comments_new'] = $newcount;
                    $context['TPortal']['article_comments_count'] = $ccount;
                }
                // if the count differs, update it
                if ($ccount != $article['comments']) {
                    $smcFunc['db_query']('', '
						UPDATE {db_prefix}tp_articles
						SET comments = {int:com}
						WHERE id = {int:artid}', array('com' => $ccount, 'artid' => $article['id']));
                }
                // the frontblocks should not display here
                $context['TPortal']['frontpanel'] = 0;
                // sort out the options
                $context['TPortal']['article']['visual_options'] = explode(',', $article['options']);
                // the custom widths
                foreach ($context['TPortal']['article']['visual_options'] as $pt) {
                    if (substr($pt, 0, 11) == 'lblockwidth') {
                        $context['TPortal']['blockwidth_left'] = substr($pt, 11);
                    }
                    if (substr($pt, 0, 11) == 'rblockwidth') {
                        $context['TPortal']['blockwidth_right'] = substr($pt, 11);
                    }
                }
                // check if no theme is to be applied
                if (in_array('nolayer', $context['TPortal']['article']['visual_options'])) {
                    $context['template_layers'] = array('nolayer');
                    // add the headers!
                    $context['tp_html_headers'] .= $article['headers'];
                }
                // set bars on/off according to options, setting override
                $all = array('showtop', 'centerpanel', 'leftpanel', 'rightpanel', 'toppanel', 'bottompanel', 'lowerpanel');
                $all2 = array('top', 'cblock', 'lblock', 'rblock', 'tblock', 'bblock', 'lbblock', 'comments', 'views', 'rating', 'date', 'title', 'commentallow', 'commentupshrink', 'ratingallow', 'nolayer', 'avatar');
                for ($p = 0; $p < 6; $p++) {
                    $primary = $context['TPortal'][$all[$p]];
                    if (in_array($all2[$p], $context['TPortal']['article']['visual_options'])) {
                        $secondary = 1;
                    } else {
                        $secondary = 0;
                    }
                    if ($primary == '1') {
                        $context['TPortal'][$all[$p]] = $secondary;
                    }
                }
                $ct = explode('|', $article['value7']);
                $cat_opts = array();
                foreach ($ct as $cc => $val) {
                    $ts = explode('=', $val);
                    if (isset($ts[0]) && isset($ts[1])) {
                        $cat_opts[$ts[0]] = $ts[1];
                    }
                }
                // decide the template
                if (isset($cat_opts['catlayout']) && $cat_opts['catlayout'] == 7) {
                    $cat_opts['template'] = $article['value9'];
                }
                $context['TPortal']['article']['category_opts'] = $cat_opts;
                // the article should follow panel settngs from category?
                if (in_array('inherit', $context['TPortal']['article']['visual_options'])) {
                    // set bars on/off according to options, setting override
                    $all = array('upperpanel', 'leftpanel', 'rightpanel', 'toppanel', 'bottompanel', 'lowerpanel');
                    for ($p = 0; $p < 5; $p++) {
                        if (isset($cat_opts[$all[$p]])) {
                            $context['TPortal'][$all[$p]] = $cat_opts[$all[$p]];
                        }
                    }
                }
                // should we supply links to articles in same category?
                if (in_array('category', $context['TPortal']['article']['visual_options'])) {
                    $request = $smcFunc['db_query']('', '
						SELECT id, subject, shortname
						FROM {db_prefix}tp_articles
						WHERE category = {int:cat}
						AND off = 0
						AND approved = 1', array('cat' => $context['TPortal']['article']['category']));
                    if ($smcFunc['db_num_rows']($request) > 0) {
                        $context['TPortal']['article']['others'] = array();
                        while ($row = $smcFunc['db_fetch_assoc']($request)) {
                            if ($row['id'] == $context['TPortal']['article']['id']) {
                                $row['selected'] = 1;
                            }
                            $context['TPortal']['article']['others'][] = $row;
                        }
                        $smcFunc['db_free_result']($request);
                    }
                }
                // can we rate this article?
                $context['TPortal']['article']['can_rate'] = in_array($context['user']['id'], explode(',', $article['voters'])) ? false : true;
                // Generate a visual verification code for comments in the article.
                if (!empty($context['TPortal']['articles_comment_captcha'])) {
                    require_once $sourcedir . '/Subs-Editor.php';
                    $verificationOptions = array('id' => 'post');
                    $context['require_verification'] = create_control_verification($verificationOptions);
                    $context['visual_verification_id'] = $verificationOptions['id'];
                }
                // are we rather printing this article and printing page is allowed?
                if (isset($_GET['print']) && $context['TPortal']['print_articles'] == 1) {
                    if (!isset($article['id'])) {
                        redirectexit();
                    }
                    $what = '<h2>' . $article['subject'] . ' </h2>' . $article['body'];
                    $pwhat = 'echo \'<h2>\' . $article[\'subject\'] . \'</h2>\';' . $article['body'];
                    if ($article['type'] == 'php') {
                        $context['TPortal']['printbody'] = eval($pwhat);
                    } elseif ($article['type'] == 'import') {
                        if (!file_exists($boarddir . '/' . $article['fileimport'])) {
                            echo '<em>', $txt['tp-cannotfetchfile'], '</em>';
                        } else {
                            include $article['fileimport'];
                        }
                        $context['TPortal']['printbody'] = '';
                    } elseif ($article['type'] == 'bbc') {
                        $context['TPortal']['printbody'] = parse_bbc($what);
                    } else {
                        $context['TPortal']['printbody'] = $what;
                    }
                    $context['TPortal']['print'] = '<a href="' . $scripturl . '?page=' . $article['id'] . '"><strong>' . $txt['tp-printgoback'] . '</strong></a>';
                    loadtemplate('TPprint');
                    $context['template_layers'] = array('tp_print');
                    $context['sub_template'] = 'tp_print_body';
                    tp_hidebars();
                }
                // linktree?
                if (!in_array('linktree', $context['TPortal']['article']['visual_options'])) {
                    $context['linktree'][0] = array('url' => '', 'name' => '');
                } else {
                    // we need the categories for the linktree
                    $allcats = array();
                    $request = $smcFunc['db_query']('', '
						SELECT * FROM {db_prefix}tp_variables
						WHERE type = {string:type}', array('type' => 'category'));
                    if ($smcFunc['db_num_rows']($request) > 0) {
                        while ($row = $smcFunc['db_fetch_assoc']($request)) {
                            $allcats[$row['id']] = $row;
                        }
                        $smcFunc['db_free_result']($request);
                    }
                    // setup the linkree
                    TPstrip_linktree();
                    // do the category have any parents?
                    $parents = array();
                    $parent = $context['TPortal']['article']['category'];
                    if (count($allcats) > 0) {
                        while ($parent != 0 && isset($allcats[$parent]['id'])) {
                            $parents[] = array('id' => $allcats[$parent]['id'], 'name' => $allcats[$parent]['value1'], 'shortname' => !empty($allcats[$parent]['value8']) ? $allcats[$parent]['value8'] : $allcats[$parent]['id']);
                            $parent = $allcats[$parent]['value2'];
                        }
                    }
                    // make the linktree
                    $parts = array_reverse($parents, TRUE);
                    // add to the linktree
                    foreach ($parts as $parent) {
                        TPadd_linktree($scripturl . '?cat=' . $parent['shortname'], $parent['name']);
                    }
                    TPadd_linktree($scripturl . '?page=' . (!empty($context['TPortal']['article']['shortname']) ? $context['TPortal']['article']['shortname'] : $context['TPortal']['article']['id']), $context['TPortal']['article']['subject']);
                }
                $context['page_title'] = $context['TPortal']['article']['subject'];
                if (WIRELESS) {
                    $context['TPortal']['single_article'] = true;
                    loadtemplate('TPwireless');
                    // decide what subtemplate
                    $context['sub_template'] = WIRELESS_PROTOCOL . '_tp_page';
                }
            }
            if (allowedTo('tp_articles')) {
                $now = time();
                if (!empty($article['pub_start']) && $article['pub_start'] > $now || !empty($article['pub_end']) && $article['pub_end'] < $now) {
                    $context['tportal']['article_expired'] = $article['id'];
                    $context['TPortal']['tperror'] = '<span class="error largetext">' . $txt['tp-expired-start'] . '</span><p>' . timeformat($article['pub_start']) . '&nbsp;&nbsp;&nbsp;-&nbsp;&nbsp;&nbsp;' . timeformat($article['pub_end']) . '</p>';
                }
            }
            return $article['id'];
        } else {
            $context['art_error'] = true;
        }
    } else {
        return;
    }
}
Beispiel #15
0
/**
 * Send it!
 */
function MessagePost2()
{
    global $txt, $context, $sourcedir;
    global $user_info, $modSettings, $scripturl, $smcFunc;
    isAllowedTo('pm_send');
    require_once $sourcedir . '/Subs-Auth.php';
    loadLanguage('PersonalMessage', '', false);
    // Extract out the spam settings - it saves database space!
    list($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
    // Initialize the errors we're about to make.
    $post_errors = array();
    // Check whether we've gone over the limit of messages we can send per hour - fatal error if fails!
    if (!empty($modSettings['pm_posts_per_hour']) && !allowedTo(array('admin_forum', 'moderate_forum', 'send_mail')) && $user_info['mod_cache']['bq'] == '0=1' && $user_info['mod_cache']['gq'] == '0=1') {
        // How many have they sent this last hour?
        $request = $smcFunc['db_query']('', '
			SELECT COUNT(pr.id_pm) AS post_count
			FROM {db_prefix}personal_messages AS pm
				INNER JOIN {db_prefix}pm_recipients AS pr ON (pr.id_pm = pm.id_pm)
			WHERE pm.id_member_from = {int:current_member}
				AND pm.msgtime > {int:msgtime}', array('current_member' => $user_info['id'], 'msgtime' => time() - 3600));
        list($postCount) = $smcFunc['db_fetch_row']($request);
        $smcFunc['db_free_result']($request);
        if (!empty($postCount) && $postCount >= $modSettings['pm_posts_per_hour']) {
            if (!isset($_REQUEST['xml'])) {
                fatal_lang_error('pm_too_many_per_hour', true, array($modSettings['pm_posts_per_hour']));
            } else {
                $post_errors[] = 'pm_too_many_per_hour';
            }
        }
    }
    // If your session timed out, show an error, but do allow to re-submit.
    if (!isset($_REQUEST['xml']) && checkSession('post', '', false) != '') {
        $post_errors[] = 'session_timeout';
    }
    $_REQUEST['subject'] = isset($_REQUEST['subject']) ? trim($_REQUEST['subject']) : '';
    $_REQUEST['to'] = empty($_POST['to']) ? empty($_GET['to']) ? '' : $_GET['to'] : $_POST['to'];
    $_REQUEST['bcc'] = empty($_POST['bcc']) ? empty($_GET['bcc']) ? '' : $_GET['bcc'] : $_POST['bcc'];
    // Route the input from the 'u' parameter to the 'to'-list.
    if (!empty($_POST['u'])) {
        $_POST['recipient_to'] = explode(',', $_POST['u']);
    }
    // Construct the list of recipients.
    $recipientList = array();
    $namedRecipientList = array();
    $namesNotFound = array();
    foreach (array('to', 'bcc') as $recipientType) {
        // First, let's see if there's user ID's given.
        $recipientList[$recipientType] = array();
        if (!empty($_POST['recipient_' . $recipientType]) && is_array($_POST['recipient_' . $recipientType])) {
            foreach ($_POST['recipient_' . $recipientType] as $recipient) {
                $recipientList[$recipientType][] = (int) $recipient;
            }
        }
        // Are there also literal names set?
        if (!empty($_REQUEST[$recipientType])) {
            // We're going to take out the "s anyway ;).
            $recipientString = strtr($_REQUEST[$recipientType], array('\\"' => '"'));
            preg_match_all('~"([^"]+)"~', $recipientString, $matches);
            $namedRecipientList[$recipientType] = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $recipientString))));
            foreach ($namedRecipientList[$recipientType] as $index => $recipient) {
                if (strlen(trim($recipient)) > 0) {
                    $namedRecipientList[$recipientType][$index] = $smcFunc['htmlspecialchars']($smcFunc['strtolower'](trim($recipient)));
                } else {
                    unset($namedRecipientList[$recipientType][$index]);
                }
            }
            if (!empty($namedRecipientList[$recipientType])) {
                $foundMembers = findMembers($namedRecipientList[$recipientType]);
                // Assume all are not found, until proven otherwise.
                $namesNotFound[$recipientType] = $namedRecipientList[$recipientType];
                foreach ($foundMembers as $member) {
                    $testNames = array($smcFunc['strtolower']($member['username']), $smcFunc['strtolower']($member['name']), $smcFunc['strtolower']($member['email']));
                    if (count(array_intersect($testNames, $namedRecipientList[$recipientType])) !== 0) {
                        $recipientList[$recipientType][] = $member['id'];
                        // Get rid of this username, since we found it.
                        $namesNotFound[$recipientType] = array_diff($namesNotFound[$recipientType], $testNames);
                    }
                }
            }
        }
        // Selected a recipient to be deleted? Remove them now.
        if (!empty($_POST['delete_recipient'])) {
            $recipientList[$recipientType] = array_diff($recipientList[$recipientType], array((int) $_POST['delete_recipient']));
        }
        // Make sure we don't include the same name twice
        $recipientList[$recipientType] = array_unique($recipientList[$recipientType]);
    }
    // Are we changing the recipients some how?
    $is_recipient_change = !empty($_POST['delete_recipient']) || !empty($_POST['to_submit']) || !empty($_POST['bcc_submit']);
    // Check if there's at least one recipient.
    if (empty($recipientList['to']) && empty($recipientList['bcc'])) {
        $post_errors[] = 'no_to';
    }
    // Make sure that we remove the members who did get it from the screen.
    if (!$is_recipient_change) {
        foreach ($recipientList as $recipientType => $dummy) {
            if (!empty($namesNotFound[$recipientType])) {
                $post_errors[] = 'bad_' . $recipientType;
                // Since we already have a post error, remove the previous one.
                $post_errors = array_diff($post_errors, array('no_to'));
                foreach ($namesNotFound[$recipientType] as $name) {
                    $context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name);
                }
            }
        }
    }
    // Did they make any mistakes?
    if ($_REQUEST['subject'] == '') {
        $post_errors[] = 'no_subject';
    }
    if (!isset($_REQUEST['message']) || $_REQUEST['message'] == '') {
        $post_errors[] = 'no_message';
    } elseif (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_REQUEST['message']) > $modSettings['max_messageLength']) {
        $post_errors[] = 'long_message';
    } else {
        // Preparse the message.
        $message = $_REQUEST['message'];
        preparsecode($message);
        // Make sure there's still some content left without the tags.
        if ($smcFunc['htmltrim'](strip_tags(parse_bbc($smcFunc['htmlspecialchars']($message, ENT_QUOTES), false), '<img>')) === '' && (!allowedTo('admin_forum') || strpos($message, '[html]') === false)) {
            $post_errors[] = 'no_message';
        }
    }
    // Wrong verification code?
    if (!$user_info['is_admin'] && !isset($_REQUEST['xml']) && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification']) {
        require_once $sourcedir . '/Subs-Editor.php';
        $verificationOptions = array('id' => 'pm');
        $context['require_verification'] = create_control_verification($verificationOptions, true);
        if (is_array($context['require_verification'])) {
            $post_errors = array_merge($post_errors, $context['require_verification']);
        }
    }
    // If they did, give a chance to make ammends.
    if (!empty($post_errors) && !$is_recipient_change && !isset($_REQUEST['preview']) && !isset($_REQUEST['xml'])) {
        return messagePostError($post_errors, $namedRecipientList, $recipientList);
    }
    // Want to take a second glance before you send?
    if (isset($_REQUEST['preview'])) {
        // Set everything up to be displayed.
        $context['preview_subject'] = $smcFunc['htmlspecialchars']($_REQUEST['subject']);
        $context['preview_message'] = $smcFunc['htmlspecialchars']($_REQUEST['message'], ENT_QUOTES);
        preparsecode($context['preview_message'], true);
        // Parse out the BBC if it is enabled.
        $context['preview_message'] = parse_bbc($context['preview_message']);
        // Censor, as always.
        censorText($context['preview_subject']);
        censorText($context['preview_message']);
        // Set a descriptive title.
        $context['page_title'] = $txt['preview'] . ' - ' . $context['preview_subject'];
        // Pretend they messed up but don't ignore if they really did :P.
        return messagePostError($post_errors, $namedRecipientList, $recipientList);
    } elseif ($is_recipient_change) {
        // Maybe we couldn't find one?
        foreach ($namesNotFound as $recipientType => $names) {
            $post_errors[] = 'bad_' . $recipientType;
            foreach ($names as $name) {
                $context['send_log']['failed'][] = sprintf($txt['pm_error_user_not_found'], $name);
            }
        }
        return messagePostError(array(), $namedRecipientList, $recipientList);
    }
    // Want to save this as a draft and think about it some more?
    if (!empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_pm_enabled']) && isset($_POST['save_draft'])) {
        require_once $sourcedir . '/Drafts.php';
        SavePMDraft($post_errors, $recipientList);
        return messagePostError($post_errors, $namedRecipientList, $recipientList);
    } elseif (!empty($modSettings['max_pm_recipients']) && count($recipientList['to']) + count($recipientList['bcc']) > $modSettings['max_pm_recipients'] && !allowedTo(array('moderate_forum', 'send_mail', 'admin_forum'))) {
        $context['send_log'] = array('sent' => array(), 'failed' => array(sprintf($txt['pm_too_many_recipients'], $modSettings['max_pm_recipients'])));
        return messagePostError($post_errors, $namedRecipientList, $recipientList);
    }
    // Protect from message spamming.
    spamProtection('pm');
    // Prevent double submission of this form.
    checkSubmitOnce('check');
    // Do the actual sending of the PM.
    if (!empty($recipientList['to']) || !empty($recipientList['bcc'])) {
        $context['send_log'] = sendpm($recipientList, $_REQUEST['subject'], $_REQUEST['message'], !empty($_REQUEST['outbox']), null, !empty($_REQUEST['pm_head']) ? (int) $_REQUEST['pm_head'] : 0);
    } else {
        $context['send_log'] = array('sent' => array(), 'failed' => array());
    }
    // Mark the message as "replied to".
    if (!empty($context['send_log']['sent']) && !empty($_REQUEST['replied_to']) && isset($_REQUEST['f']) && $_REQUEST['f'] == 'inbox') {
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}pm_recipients
			SET is_read = is_read | 2
			WHERE id_pm = {int:replied_to}
				AND id_member = {int:current_member}', array('current_member' => $user_info['id'], 'replied_to' => (int) $_REQUEST['replied_to']));
    }
    // If one or more of the recipient were invalid, go back to the post screen with the failed usernames.
    if (!empty($context['send_log']['failed'])) {
        return messagePostError($post_errors, $namesNotFound, array('to' => array_intersect($recipientList['to'], $context['send_log']['failed']), 'bcc' => array_intersect($recipientList['bcc'], $context['send_log']['failed'])));
    }
    // Message sent successfully?
    if (!empty($context['send_log']) && empty($context['send_log']['failed'])) {
        $context['current_label_redirect'] = $context['current_label_redirect'] . ';done=sent';
    }
    // Go back to the where they sent from, if possible...
    redirectexit($context['current_label_redirect']);
}
Beispiel #16
0
    /**
     * The central part of the board - topic display.
     *
     * What it does:
     * - This function loads the posts in a topic up so they can be displayed.
     * - It uses the main sub template of the Display template.
     * - It requires a topic, and can go to the previous or next topic from it.
     * - It jumps to the correct post depending on a number/time/IS_MSG passed.
     * - It depends on the messages_per_page, defaultMaxMessages and enableAllMessages settings.
     * - It is accessed by ?topic=id_topic.START.
     */
    public function action_display()
    {
        global $scripturl, $txt, $modSettings, $context, $settings;
        global $options, $user_info, $board_info, $topic, $board;
        global $attachments, $messages_request;
        // What are you gonna display if these are empty?!
        if (empty($topic)) {
            fatal_lang_error('no_board', false);
        }
        // Load the template
        loadTemplate('Display');
        $context['sub_template'] = 'messages';
        // And the topic functions
        require_once SUBSDIR . '/Topic.subs.php';
        require_once SUBSDIR . '/Messages.subs.php';
        // Not only does a prefetch make things slower for the server, but it makes it impossible to know if they read it.
        if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') {
            @ob_end_clean();
            header('HTTP/1.1 403 Prefetch Forbidden');
            die;
        }
        // How much are we sticking on each page?
        $context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
        $template_layers = Template_Layers::getInstance();
        $template_layers->addEnd('messages_informations');
        $includeUnapproved = !$modSettings['postmod_active'] || allowedTo('approve_posts');
        // Let's do some work on what to search index.
        if (count($_GET) > 2) {
            foreach ($_GET as $k => $v) {
                if (!in_array($k, array('topic', 'board', 'start', session_name()))) {
                    $context['robot_no_index'] = true;
                }
            }
        }
        if (!empty($_REQUEST['start']) && (!is_numeric($_REQUEST['start']) || $_REQUEST['start'] % $context['messages_per_page'] != 0)) {
            $context['robot_no_index'] = true;
        }
        // Find the previous or next topic.  Make a fuss if there are no more.
        if (isset($_REQUEST['prev_next']) && ($_REQUEST['prev_next'] == 'prev' || $_REQUEST['prev_next'] == 'next')) {
            // No use in calculating the next topic if there's only one.
            if ($board_info['num_topics'] > 1) {
                $includeStickies = !empty($modSettings['enableStickyTopics']);
                $topic = $_REQUEST['prev_next'] === 'prev' ? previousTopic($topic, $board, $user_info['id'], $includeUnapproved, $includeStickies) : nextTopic($topic, $board, $user_info['id'], $includeUnapproved, $includeStickies);
                $context['current_topic'] = $topic;
            }
            // Go to the newest message on this topic.
            $_REQUEST['start'] = 'new';
        }
        // Add 1 to the number of views of this topic (except for robots).
        if (!$user_info['possibly_robot'] && (empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic)) {
            increaseViewCounter($topic);
            $_SESSION['last_read_topic'] = $topic;
        }
        $topic_selects = array();
        $topic_tables = array();
        $topic_parameters = array('topic' => $topic, 'member' => $user_info['id'], 'board' => (int) $board);
        // Allow addons to add additional details to the topic query
        call_integration_hook('integrate_topic_query', array(&$topic_selects, &$topic_tables, &$topic_parameters));
        // Load the topic details
        $topicinfo = getTopicInfo($topic_parameters, 'all', $topic_selects, $topic_tables);
        if (empty($topicinfo)) {
            fatal_lang_error('not_a_topic', false);
        }
        // Is this a moved topic that we are redirecting to?
        if (!empty($topicinfo['id_redirect_topic']) && !isset($_GET['noredir'])) {
            markTopicsRead(array($user_info['id'], $topic, $topicinfo['id_last_msg'], 0), $topicinfo['new_from'] !== 0);
            redirectexit('topic=' . $topicinfo['id_redirect_topic'] . '.0;redirfrom=' . $topicinfo['id_topic']);
        }
        $context['real_num_replies'] = $context['num_replies'] = $topicinfo['num_replies'];
        $context['topic_first_message'] = $topicinfo['id_first_msg'];
        $context['topic_last_message'] = $topicinfo['id_last_msg'];
        $context['topic_unwatched'] = isset($topicinfo['unwatched']) ? $topicinfo['unwatched'] : 0;
        if (isset($_GET['redirfrom'])) {
            $redir_topics = topicsList(array((int) $_GET['redirfrom']));
            if (!empty($redir_topics[(int) $_GET['redirfrom']])) {
                $context['topic_redirected_from'] = $redir_topics[(int) $_GET['redirfrom']];
                $context['topic_redirected_from']['redir_href'] = $scripturl . '?topic=' . $context['topic_redirected_from']['id_topic'] . '.0;noredir';
            }
        }
        // Add up unapproved replies to get real number of replies...
        if ($modSettings['postmod_active'] && allowedTo('approve_posts')) {
            $context['real_num_replies'] += $topicinfo['unapproved_posts'] - ($topicinfo['approved'] ? 0 : 1);
        }
        // If this topic was derived from another, set the followup details
        if (!empty($topicinfo['derived_from'])) {
            require_once SUBSDIR . '/FollowUps.subs.php';
            $context['topic_derived_from'] = topicStartedHere($topic, $includeUnapproved);
        }
        // If this topic has unapproved posts, we need to work out how many posts the user can see, for page indexing.
        if (!$includeUnapproved && $topicinfo['unapproved_posts'] && !$user_info['is_guest']) {
            $myUnapprovedPosts = unapprovedPosts($topic, $user_info['id']);
            $context['total_visible_posts'] = $context['num_replies'] + $myUnapprovedPosts + ($topicinfo['approved'] ? 1 : 0);
        } elseif ($user_info['is_guest']) {
            $context['total_visible_posts'] = $context['num_replies'] + ($topicinfo['approved'] ? 1 : 0);
        } else {
            $context['total_visible_posts'] = $context['num_replies'] + $topicinfo['unapproved_posts'] + ($topicinfo['approved'] ? 1 : 0);
        }
        // When was the last time this topic was replied to?  Should we warn them about it?
        if (!empty($modSettings['oldTopicDays'])) {
            $mgsOptions = basicMessageInfo($topicinfo['id_last_msg'], true);
            $context['oldTopicError'] = $mgsOptions['poster_time'] + $modSettings['oldTopicDays'] * 86400 < time() && empty($topicinfo['is_sticky']);
        } else {
            $context['oldTopicError'] = false;
        }
        // The start isn't a number; it's information about what to do, where to go.
        if (!is_numeric($_REQUEST['start'])) {
            // Redirect to the page and post with new messages, originally by Omar Bazavilvazo.
            if ($_REQUEST['start'] == 'new') {
                // Guests automatically go to the last post.
                if ($user_info['is_guest']) {
                    $context['start_from'] = $context['total_visible_posts'] - 1;
                    $_REQUEST['start'] = $context['start_from'];
                } else {
                    // Fall through to the next if statement.
                    $_REQUEST['start'] = 'msg' . $topicinfo['new_from'];
                }
            }
            // Start from a certain time index, not a message.
            if (substr($_REQUEST['start'], 0, 4) == 'from') {
                $timestamp = (int) substr($_REQUEST['start'], 4);
                if ($timestamp === 0) {
                    $_REQUEST['start'] = 0;
                } else {
                    // Find the number of messages posted before said time...
                    $context['start_from'] = countNewPosts($topic, $topicinfo, $timestamp);
                    $_REQUEST['start'] = $context['start_from'];
                }
            } elseif (substr($_REQUEST['start'], 0, 3) == 'msg') {
                $virtual_msg = (int) substr($_REQUEST['start'], 3);
                if (!$topicinfo['unapproved_posts'] && $virtual_msg >= $topicinfo['id_last_msg']) {
                    $context['start_from'] = $context['total_visible_posts'] - 1;
                } elseif (!$topicinfo['unapproved_posts'] && $virtual_msg <= $topicinfo['id_first_msg']) {
                    $context['start_from'] = 0;
                } else {
                    $only_approved = $modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts');
                    $context['start_from'] = countMessagesBefore($topic, $virtual_msg, false, $only_approved, !$user_info['is_guest']);
                }
                // We need to reverse the start as well in this case.
                $_REQUEST['start'] = $context['start_from'];
            }
        }
        // Mark the mention as read if requested
        if (isset($_REQUEST['mentionread']) && !empty($virtual_msg)) {
            require_once CONTROLLERDIR . '/Mentions.controller.php';
            $mentions = new Mentions_Controller();
            $mentions->setData(array('id_mention' => $_REQUEST['item'], 'mark' => $_REQUEST['mark']));
            $mentions->action_markread();
        }
        // Create a previous next string if the selected theme has it as a selected option.
        if ($modSettings['enablePreviousNext']) {
            $context['links'] += array('go_prev' => $scripturl . '?topic=' . $topic . '.0;prev_next=prev#new', 'go_next' => $scripturl . '?topic=' . $topic . '.0;prev_next=next#new');
        }
        // Derived from, set the link back
        if (!empty($context['topic_derived_from'])) {
            $context['links']['derived_from'] = $scripturl . '?msg=' . $context['topic_derived_from']['derived_from'];
        }
        // Check if spellchecking is both enabled and actually working. (for quick reply.)
        $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new');
        if ($context['show_spellchecking']) {
            loadJavascriptFile('spellcheck.js', array('defer' => true));
        }
        // Do we need to show the visual verification image?
        $context['require_verification'] = !$user_info['is_mod'] && !$user_info['is_admin'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || $user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1);
        if ($context['require_verification']) {
            require_once SUBSDIR . '/VerificationControls.class.php';
            $verificationOptions = array('id' => 'post');
            $context['require_verification'] = create_control_verification($verificationOptions);
            $context['visual_verification_id'] = $verificationOptions['id'];
        }
        // Are we showing signatures - or disabled fields?
        $context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1;
        $context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
        // Censor the title...
        censorText($topicinfo['subject']);
        $context['page_title'] = $topicinfo['subject'];
        // Is this topic sticky, or can it even be?
        $topicinfo['is_sticky'] = empty($modSettings['enableStickyTopics']) ? '0' : $topicinfo['is_sticky'];
        // Allow addons access to the topicinfo array
        call_integration_hook('integrate_display_topic', array($topicinfo));
        // Default this topic to not marked for notifications... of course...
        $context['is_marked_notify'] = false;
        // Did we report a post to a moderator just now?
        $context['report_sent'] = isset($_GET['reportsent']);
        if ($context['report_sent']) {
            $template_layers->add('report_sent');
        }
        // Let's get nosey, who is viewing this topic?
        if (!empty($settings['display_who_viewing'])) {
            require_once SUBSDIR . '/Who.subs.php';
            formatViewers($topic, 'topic');
        }
        // If all is set, but not allowed... just unset it.
        $can_show_all = !empty($modSettings['enableAllMessages']) && $context['total_visible_posts'] > $context['messages_per_page'] && $context['total_visible_posts'] < $modSettings['enableAllMessages'];
        if (isset($_REQUEST['all']) && !$can_show_all) {
            unset($_REQUEST['all']);
        } elseif (isset($_REQUEST['all'])) {
            $_REQUEST['start'] = -1;
        }
        // Construct the page index, allowing for the .START method...
        $context['page_index'] = constructPageIndex($scripturl . '?topic=' . $topic . '.%1$d', $_REQUEST['start'], $context['total_visible_posts'], $context['messages_per_page'], true, array('all' => $can_show_all, 'all_selected' => isset($_REQUEST['all'])));
        $context['start'] = $_REQUEST['start'];
        // This is information about which page is current, and which page we're on - in case you don't like the constructed page index. (again, wireles..)
        $context['page_info'] = array('current_page' => $_REQUEST['start'] / $context['messages_per_page'] + 1, 'num_pages' => floor(($context['total_visible_posts'] - 1) / $context['messages_per_page']) + 1);
        // Figure out all the link to the next/prev
        $context['links'] += array('prev' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] - $context['messages_per_page']) : '', 'next' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] + $context['messages_per_page']) : '');
        // If they are viewing all the posts, show all the posts, otherwise limit the number.
        if ($can_show_all && isset($_REQUEST['all'])) {
            // No limit! (actually, there is a limit, but...)
            $context['messages_per_page'] = -1;
            // Set start back to 0...
            $_REQUEST['start'] = 0;
        }
        // Build the link tree.
        $context['linktree'][] = array('url' => $scripturl . '?topic=' . $topic . '.0', 'name' => $topicinfo['subject']);
        // Build a list of this board's moderators.
        $context['moderators'] =& $board_info['moderators'];
        $context['link_moderators'] = array();
        // Information about the current topic...
        $context['is_locked'] = $topicinfo['locked'];
        $context['is_sticky'] = $topicinfo['is_sticky'];
        $context['is_very_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicVeryPosts'];
        $context['is_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicPosts'];
        $context['is_approved'] = $topicinfo['approved'];
        $context['is_poll'] = $topicinfo['id_poll'] > 0 && !empty($modSettings['pollMode']) && allowedTo('poll_view');
        determineTopicClass($context);
        // Did this user start the topic or not?
        $context['user']['started'] = $user_info['id'] == $topicinfo['id_member_started'] && !$user_info['is_guest'];
        $context['topic_starter_id'] = $topicinfo['id_member_started'];
        // Set the topic's information for the template.
        $context['subject'] = $topicinfo['subject'];
        $context['num_views'] = $topicinfo['num_views'];
        $context['num_views_text'] = $context['num_views'] == 1 ? $txt['read_one_time'] : sprintf($txt['read_many_times'], $context['num_views']);
        $context['mark_unread_time'] = !empty($virtual_msg) ? $virtual_msg : $topicinfo['new_from'];
        // Set a canonical URL for this page.
        $context['canonical_url'] = $scripturl . '?topic=' . $topic . '.' . $context['start'];
        // For quick reply we need a response prefix in the default forum language.
        $context['response_prefix'] = response_prefix();
        // If we want to show event information in the topic, prepare the data.
        if (allowedTo('calendar_view') && !empty($modSettings['cal_showInTopic']) && !empty($modSettings['cal_enabled'])) {
            // We need events details and all that jazz
            require_once SUBSDIR . '/Calendar.subs.php';
            // First, try create a better time format, ignoring the "time" elements.
            if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0])) {
                $date_string = $user_info['time_format'];
            } else {
                $date_string = $matches[0];
            }
            // Get event information for this topic.
            $events = eventInfoForTopic($topic);
            $context['linked_calendar_events'] = array();
            foreach ($events as $event) {
                // Prepare the dates for being formatted.
                $start_date = sscanf($event['start_date'], '%04d-%02d-%02d');
                $start_date = mktime(12, 0, 0, $start_date[1], $start_date[2], $start_date[0]);
                $end_date = sscanf($event['end_date'], '%04d-%02d-%02d');
                $end_date = mktime(12, 0, 0, $end_date[1], $end_date[2], $end_date[0]);
                $context['linked_calendar_events'][] = array('id' => $event['id_event'], 'title' => $event['title'], 'can_edit' => allowedTo('calendar_edit_any') || $event['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own'), 'modify_href' => $scripturl . '?action=post;msg=' . $topicinfo['id_first_msg'] . ';topic=' . $topic . '.0;calendar;eventid=' . $event['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'can_export' => allowedTo('calendar_edit_any') || $event['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own'), 'export_href' => $scripturl . '?action=calendar;sa=ical;eventid=' . $event['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'start_date' => standardTime($start_date, $date_string, 'none'), 'start_timestamp' => $start_date, 'end_date' => standardTime($end_date, $date_string, 'none'), 'end_timestamp' => $end_date, 'is_last' => false);
            }
            if (!empty($context['linked_calendar_events'])) {
                $context['linked_calendar_events'][count($context['linked_calendar_events']) - 1]['is_last'] = true;
                $template_layers->add('display_calendar');
            }
        }
        // Create the poll info if it exists.
        if ($context['is_poll']) {
            $template_layers->add('display_poll');
            require_once SUBSDIR . '/Poll.subs.php';
            loadPollContext($topicinfo['id_poll']);
            // Build the poll moderation button array.
            $context['poll_buttons'] = array('vote' => array('test' => 'allow_return_vote', 'text' => 'poll_return_vote', 'image' => 'poll_options.png', 'lang' => true, 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start']), 'results' => array('test' => 'allow_poll_view', 'text' => 'poll_results', 'image' => 'poll_results.png', 'lang' => true, 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start'] . ';viewresults'), 'change_vote' => array('test' => 'allow_change_vote', 'text' => 'poll_change_vote', 'image' => 'poll_change_vote.png', 'lang' => true, 'url' => $scripturl . '?action=poll;sa=vote;topic=' . $context['current_topic'] . '.' . $context['start'] . ';poll=' . $context['poll']['id'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'lock' => array('test' => 'allow_lock_poll', 'text' => !$context['poll']['is_locked'] ? 'poll_lock' : 'poll_unlock', 'image' => 'poll_lock.png', 'lang' => true, 'url' => $scripturl . '?action=lockvoting;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'edit' => array('test' => 'allow_edit_poll', 'text' => 'poll_edit', 'image' => 'poll_edit.png', 'lang' => true, 'url' => $scripturl . '?action=editpoll;topic=' . $context['current_topic'] . '.' . $context['start']), 'remove_poll' => array('test' => 'can_remove_poll', 'text' => 'poll_remove', 'image' => 'admin_remove_poll.png', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . $txt['poll_remove_warn'] . '\');"', 'url' => $scripturl . '?action=poll;sa=remove;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']));
            // Allow mods to add additional buttons here
            call_integration_hook('integrate_poll_buttons');
        }
        // Calculate the fastest way to get the messages!
        $ascending = true;
        $start = $_REQUEST['start'];
        $limit = $context['messages_per_page'];
        $firstIndex = 0;
        if ($start >= $context['total_visible_posts'] / 2 && $context['messages_per_page'] != -1) {
            $ascending = !$ascending;
            $limit = $context['total_visible_posts'] <= $start + $limit ? $context['total_visible_posts'] - $start : $limit;
            $start = $context['total_visible_posts'] <= $start + $limit ? 0 : $context['total_visible_posts'] - $start - $limit;
            $firstIndex = $limit - 1;
        }
        // Taking care of member specific settings
        $limit_settings = array('messages_per_page' => $context['messages_per_page'], 'start' => $start, 'offset' => $limit);
        // Get each post and poster in this topic.
        $topic_details = getTopicsPostsAndPoster($topic, $limit_settings, $ascending);
        $messages = $topic_details['messages'];
        $posters = array_unique($topic_details['all_posters']);
        $all_posters = $topic_details['all_posters'];
        unset($topic_details);
        call_integration_hook('integrate_display_message_list', array(&$messages, &$posters));
        // Guests can't mark topics read or for notifications, just can't sorry.
        if (!$user_info['is_guest'] && !empty($messages)) {
            $mark_at_msg = max($messages);
            if ($mark_at_msg >= $topicinfo['id_last_msg']) {
                $mark_at_msg = $modSettings['maxMsgID'];
            }
            if ($mark_at_msg >= $topicinfo['new_from']) {
                markTopicsRead(array($user_info['id'], $topic, $mark_at_msg, $topicinfo['unwatched']), $topicinfo['new_from'] !== 0);
            }
            updateReadNotificationsFor($topic, $board);
            // Have we recently cached the number of new topics in this board, and it's still a lot?
            if (isset($_REQUEST['topicseen']) && isset($_SESSION['topicseen_cache'][$board]) && $_SESSION['topicseen_cache'][$board] > 5) {
                $_SESSION['topicseen_cache'][$board]--;
            } elseif (isset($_REQUEST['topicseen'])) {
                // Use the mark read tables... and the last visit to figure out if this should be read or not.
                $numNewTopics = getUnreadCountSince($board, empty($_SESSION['id_msg_last_visit']) ? 0 : $_SESSION['id_msg_last_visit']);
                // If there're no real new topics in this board, mark the board as seen.
                if (empty($numNewTopics)) {
                    $_REQUEST['boardseen'] = true;
                } else {
                    $_SESSION['topicseen_cache'][$board] = $numNewTopics;
                }
            } elseif (isset($_SESSION['topicseen_cache'][$board])) {
                $_SESSION['topicseen_cache'][$board]--;
            }
            // Mark board as seen if we came using last post link from BoardIndex. (or other places...)
            if (isset($_REQUEST['boardseen'])) {
                require_once SUBSDIR . '/Boards.subs.php';
                markBoardsRead($board, false, false);
            }
        }
        $attachments = array();
        // If there _are_ messages here... (probably an error otherwise :!)
        if (!empty($messages)) {
            require_once SUBSDIR . '/Attachments.subs.php';
            // Fetch attachments.
            $includeUnapproved = !$modSettings['postmod_active'] || allowedTo('approve_posts');
            if (!empty($modSettings['attachmentEnable']) && allowedTo('view_attachments')) {
                $attachments = getAttachments($messages, $includeUnapproved, 'filter_accessible_attachment', $all_posters);
            }
            $msg_parameters = array('message_list' => $messages, 'new_from' => $topicinfo['new_from']);
            $msg_selects = array();
            $msg_tables = array();
            call_integration_hook('integrate_message_query', array(&$msg_selects, &$msg_tables, &$msg_parameters));
            // What?  It's not like it *couldn't* be only guests in this topic...
            if (!empty($posters)) {
                loadMemberData($posters);
            }
            // Load in the likes for this group of messages
            if (!empty($modSettings['likes_enabled'])) {
                require_once SUBSDIR . '/Likes.subs.php';
                $context['likes'] = loadLikes($messages, true);
                // ajax controller for likes
                loadJavascriptFile('like_posts.js', array('defer' => true));
                loadLanguage('Errors');
                // Initiate likes and the tooltips for likes
                addInlineJavascript('
				$(document).ready(function () {
					var likePostInstance = likePosts.prototype.init({
						oTxt: ({
							btnText : ' . JavaScriptEscape($txt['ok_uppercase']) . ',
							likeHeadingError : ' . JavaScriptEscape($txt['like_heading_error']) . ',
							error_occurred : ' . JavaScriptEscape($txt['error_occurred']) . '
						}),
					});

					$(".like_button, .unlike_button").SiteTooltip({
						hoverIntent: {
							sensitivity: 10,
							interval: 150,
							timeout: 50
						}
					});
				});', true);
            }
            $messages_request = loadMessageRequest($msg_selects, $msg_tables, $msg_parameters);
            if (!empty($modSettings['enableFollowup'])) {
                require_once SUBSDIR . '/FollowUps.subs.php';
                $context['follow_ups'] = followupTopics($messages, $includeUnapproved);
            }
            // Go to the last message if the given time is beyond the time of the last message.
            if (isset($context['start_from']) && $context['start_from'] >= $topicinfo['num_replies']) {
                $context['start_from'] = $topicinfo['num_replies'];
            }
            // Since the anchor information is needed on the top of the page we load these variables beforehand.
            $context['first_message'] = isset($messages[$firstIndex]) ? $messages[$firstIndex] : $messages[0];
            $context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $context['start_from'];
        } else {
            $messages_request = false;
            $context['first_message'] = 0;
            $context['first_new_message'] = false;
        }
        $context['jump_to'] = array('label' => addslashes(un_htmlspecialchars($txt['jump_to'])), 'board_name' => htmlspecialchars(strtr(strip_tags($board_info['name']), array('&amp;' => '&')), ENT_COMPAT, 'UTF-8'), 'child_level' => $board_info['child_level']);
        // Set the callback.  (do you REALIZE how much memory all the messages would take?!?)
        // This will be called from the template.
        $context['get_message'] = array($this, 'prepareDisplayContext_callback');
        // Now set all the wonderful, wonderful permissions... like moderation ones...
        $common_permissions = array('can_approve' => 'approve_posts', 'can_ban' => 'manage_bans', 'can_sticky' => 'make_sticky', 'can_merge' => 'merge_any', 'can_split' => 'split_any', 'calendar_post' => 'calendar_post', 'can_mark_notify' => 'mark_any_notify', 'can_send_topic' => 'send_topic', 'can_send_pm' => 'pm_send', 'can_send_email' => 'send_email_to_members', 'can_report_moderator' => 'report_any', 'can_moderate_forum' => 'moderate_forum', 'can_issue_warning' => 'issue_warning', 'can_restore_topic' => 'move_any', 'can_restore_msg' => 'move_any');
        foreach ($common_permissions as $contextual => $perm) {
            $context[$contextual] = allowedTo($perm);
        }
        // Permissions with _any/_own versions.  $context[YYY] => ZZZ_any/_own.
        $anyown_permissions = array('can_move' => 'move', 'can_lock' => 'lock', 'can_delete' => 'remove', 'can_add_poll' => 'poll_add', 'can_remove_poll' => 'poll_remove', 'can_reply' => 'post_reply', 'can_reply_unapproved' => 'post_unapproved_replies');
        foreach ($anyown_permissions as $contextual => $perm) {
            $context[$contextual] = allowedTo($perm . '_any') || $context['user']['started'] && allowedTo($perm . '_own');
        }
        // Cleanup all the permissions with extra stuff...
        $context['can_mark_notify'] &= !$context['user']['is_guest'];
        $context['can_sticky'] &= !empty($modSettings['enableStickyTopics']);
        $context['calendar_post'] &= !empty($modSettings['cal_enabled']) && (allowedTo('modify_any') || $context['user']['started'] && allowedTo('modify_own'));
        $context['can_add_poll'] &= !empty($modSettings['pollMode']) && $topicinfo['id_poll'] <= 0;
        $context['can_remove_poll'] &= !empty($modSettings['pollMode']) && $topicinfo['id_poll'] > 0;
        $context['can_reply'] &= empty($topicinfo['locked']) || allowedTo('moderate_board');
        $context['can_reply_unapproved'] &= $modSettings['postmod_active'] && (empty($topicinfo['locked']) || allowedTo('moderate_board'));
        $context['can_issue_warning'] &= in_array('w', $context['admin_features']) && !empty($modSettings['warning_enable']);
        // Handle approval flags...
        $context['can_reply_approved'] = $context['can_reply'];
        $context['can_reply'] |= $context['can_reply_unapproved'];
        $context['can_quote'] = $context['can_reply'] && (empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])));
        $context['can_mark_unread'] = !$user_info['is_guest'] && $settings['show_mark_read'];
        $context['can_unwatch'] = !$user_info['is_guest'] && $modSettings['enable_unwatch'];
        $context['can_send_topic'] = (!$modSettings['postmod_active'] || $topicinfo['approved']) && allowedTo('send_topic');
        $context['can_print'] = empty($modSettings['disable_print_topic']);
        // Start this off for quick moderation - it will be or'd for each post.
        $context['can_remove_post'] = allowedTo('delete_any') || allowedTo('delete_replies') && $context['user']['started'];
        // Can restore topic?  That's if the topic is in the recycle board and has a previous restore state.
        $context['can_restore_topic'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_board']);
        $context['can_restore_msg'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_topic']);
        $context['can_follow_up'] = !empty($modSettings['enableFollowup']) && boardsallowedto('post_new') !== array();
        // Check if the draft functions are enabled and that they have permission to use them (for quick reply.)
        $context['drafts_save'] = !empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft') && $context['can_reply'];
        $context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('post_autosave_draft');
        if (!empty($context['drafts_save'])) {
            loadLanguage('Drafts');
        }
        if (!empty($context['drafts_autosave']) && empty($options['use_editor_quick_reply'])) {
            loadJavascriptFile('drafts.js');
        }
        if (!empty($modSettings['mentions_enabled'])) {
            $context['mentions_enabled'] = true;
            // Just using the plain text quick reply and not the editor
            if (empty($options['use_editor_quick_reply'])) {
                loadJavascriptFile(array('jquery.atwho.js', 'jquery.caret.min.js', 'mentioning.js'));
            }
            loadCSSFile('jquery.atwho.css');
            addInlineJavascript('
			$(document).ready(function () {
				for (var i = 0, count = all_elk_mentions.length; i < count; i++)
					all_elk_mentions[i].oMention = new elk_mentions(all_elk_mentions[i].oOptions);
			});');
        }
        // Load up the Quick ModifyTopic and Quick Reply scripts
        loadJavascriptFile('topic.js');
        // Auto video embeding enabled?
        if (!empty($modSettings['enableVideoEmbeding'])) {
            addInlineJavascript('
		$(document).ready(function() {
			$().linkifyvideo(oEmbedtext);
		});');
        }
        // Load up the "double post" sequencing magic.
        if (!empty($options['display_quick_reply'])) {
            checkSubmitOnce('register');
            $context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
            $context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
            if (!empty($options['use_editor_quick_reply']) && $context['can_reply']) {
                // Needed for the editor and message icons.
                require_once SUBSDIR . '/Editor.subs.php';
                // Now create the editor.
                $editorOptions = array('id' => 'message', 'value' => '', 'labels' => array('post_button' => $txt['post']), 'height' => '250px', 'width' => '100%', 'preview_type' => 0);
                create_control_richedit($editorOptions);
                $context['attached'] = '';
                $context['make_poll'] = isset($_REQUEST['poll']);
                // Message icons - customized icons are off?
                $context['icons'] = getMessageIcons($board);
                if (!empty($context['icons'])) {
                    $context['icons'][count($context['icons']) - 1]['is_last'] = true;
                }
            }
        }
        addJavascriptVar(array('notification_topic_notice' => $context['is_marked_notify'] ? $txt['notification_disable_topic'] : $txt['notification_enable_topic']), true);
        if ($context['can_send_topic']) {
            addJavascriptVar(array('sendtopic_cancel' => $txt['modify_cancel'], 'sendtopic_back' => $txt['back'], 'sendtopic_close' => $txt['find_close'], 'sendtopic_error' => $txt['send_error_occurred'], 'required_field' => $txt['require_field']), true);
        }
        // Build the normal button array.
        $context['normal_buttons'] = array('reply' => array('test' => 'can_reply', 'text' => 'reply', 'image' => 'reply.png', 'lang' => true, 'url' => $scripturl . '?action=post;topic=' . $context['current_topic'] . '.' . $context['start'] . ';last_msg=' . $context['topic_last_message'], 'active' => true), 'notify' => array('test' => 'can_mark_notify', 'text' => $context['is_marked_notify'] ? 'unnotify' : 'notify', 'image' => ($context['is_marked_notify'] ? 'un' : '') . 'notify.png', 'lang' => true, 'custom' => 'onclick="return notifyButton(this);"', 'url' => $scripturl . '?action=notify;sa=' . ($context['is_marked_notify'] ? 'off' : 'on') . ';topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'mark_unread' => array('test' => 'can_mark_unread', 'text' => 'mark_unread', 'image' => 'markunread.png', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=topic;t=' . $context['mark_unread_time'] . ';topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'unwatch' => array('test' => 'can_unwatch', 'text' => ($context['topic_unwatched'] ? '' : 'un') . 'watch', 'image' => ($context['topic_unwatched'] ? '' : 'un') . 'watched.png', 'lang' => true, 'custom' => 'onclick="return unwatchButton(this);"', 'url' => $scripturl . '?action=unwatchtopic;topic=' . $context['current_topic'] . '.' . $context['start'] . ';sa=' . ($context['topic_unwatched'] ? 'off' : 'on') . ';' . $context['session_var'] . '=' . $context['session_id']), 'send' => array('test' => 'can_send_topic', 'text' => 'send_topic', 'image' => 'sendtopic.png', 'lang' => true, 'url' => $scripturl . '?action=emailuser;sa=sendtopic;topic=' . $context['current_topic'] . '.0', 'custom' => 'onclick="return sendtopicOverlayDiv(this.href, \'' . $txt['send_topic'] . '\');"'), 'print' => array('test' => 'can_print', 'text' => 'print', 'image' => 'print.png', 'lang' => true, 'custom' => 'rel="nofollow"', 'class' => 'new_win', 'url' => $scripturl . '?action=topic;sa=printpage;topic=' . $context['current_topic'] . '.0'));
        // Build the mod button array
        $context['mod_buttons'] = array('move' => array('test' => 'can_move', 'text' => 'move_topic', 'image' => 'admin_move.png', 'lang' => true, 'url' => $scripturl . '?action=movetopic;current_board=' . $context['current_board'] . ';topic=' . $context['current_topic'] . '.0'), 'delete' => array('test' => 'can_delete', 'text' => 'remove_topic', 'image' => 'admin_rem.png', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . $txt['are_sure_remove_topic'] . '\');"', 'url' => $scripturl . '?action=removetopic2;topic=' . $context['current_topic'] . '.0;' . $context['session_var'] . '=' . $context['session_id']), 'lock' => array('test' => 'can_lock', 'text' => empty($context['is_locked']) ? 'set_lock' : 'set_unlock', 'image' => 'admin_lock.png', 'lang' => true, 'url' => $scripturl . '?action=topic;sa=lock;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'sticky' => array('test' => 'can_sticky', 'text' => empty($context['is_sticky']) ? 'set_sticky' : 'set_nonsticky', 'image' => 'admin_sticky.png', 'lang' => true, 'url' => $scripturl . '?action=topic;sa=sticky;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'merge' => array('test' => 'can_merge', 'text' => 'merge', 'image' => 'merge.png', 'lang' => true, 'url' => $scripturl . '?action=mergetopics;board=' . $context['current_board'] . '.0;from=' . $context['current_topic']), 'calendar' => array('test' => 'calendar_post', 'text' => 'calendar_link', 'image' => 'linktocal.png', 'lang' => true, 'url' => $scripturl . '?action=post;calendar;msg=' . $context['topic_first_message'] . ';topic=' . $context['current_topic'] . '.0'));
        // Restore topic. eh?  No monkey business.
        if ($context['can_restore_topic']) {
            $context['mod_buttons'][] = array('text' => 'restore_topic', 'image' => '', 'lang' => true, 'url' => $scripturl . '?action=restoretopic;topics=' . $context['current_topic'] . ';' . $context['session_var'] . '=' . $context['session_id']);
        }
        if ($context['can_reply'] && !empty($options['display_quick_reply'])) {
            $template_layers->add('quickreply');
        }
        $template_layers->add('pages_and_buttons');
        // Allow adding new buttons easily.
        call_integration_hook('integrate_display_buttons');
        call_integration_hook('integrate_mod_buttons');
    }
function Display()
{
    global $scripturl, $txt, $modSettings, $context, $settings;
    global $options, $sourcedir, $user_info, $board_info, $topic, $board;
    global $attachments, $messages_request, $topicinfo, $language, $smcFunc;
    // What are you gonna display if these are empty?!
    if (empty($topic)) {
        fatal_lang_error('no_board', 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;
    }
    // How much are we sticking on each page?
    $context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) && !WIRELESS ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
    // Let's do some work on what to search index.
    if (count($_GET) > 2) {
        foreach ($_GET as $k => $v) {
            if (!in_array($k, array('topic', 'board', 'start', session_name()))) {
                $context['robot_no_index'] = true;
            }
        }
    }
    if (!empty($_REQUEST['start']) && (!is_numeric($_REQUEST['start']) || $_REQUEST['start'] % $context['messages_per_page'] != 0)) {
        $context['robot_no_index'] = true;
    }
    // Find the previous or next topic.  Make a fuss if there are no more.
    if (isset($_REQUEST['prev_next']) && ($_REQUEST['prev_next'] == 'prev' || $_REQUEST['prev_next'] == 'next')) {
        // No use in calculating the next topic if there's only one.
        if ($board_info['num_topics'] > 1) {
            // Just prepare some variables that are used in the query.
            $gt_lt = $_REQUEST['prev_next'] == 'prev' ? '>' : '<';
            $order = $_REQUEST['prev_next'] == 'prev' ? '' : ' DESC';
            $request = $smcFunc['db_query']('', '
				SELECT t2.id_topic
				FROM {db_prefix}topics AS t
					INNER JOIN {db_prefix}topics AS t2 ON (' . (empty($modSettings['enableStickyTopics']) ? '
					t2.id_last_msg ' . $gt_lt . ' t.id_last_msg' : '
					(t2.id_last_msg ' . $gt_lt . ' t.id_last_msg AND t2.is_sticky ' . $gt_lt . '= t.is_sticky) OR t2.is_sticky ' . $gt_lt . ' t.is_sticky') . ')
				WHERE t.id_topic = {int:current_topic}
					AND t2.id_board = {int:current_board}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
					AND (t2.approved = {int:is_approved} OR (t2.id_member_started != {int:id_member_started} AND t2.id_member_started = {int:current_member}))') . '
				ORDER BY' . (empty($modSettings['enableStickyTopics']) ? '' : ' t2.is_sticky' . $order . ',') . ' t2.id_last_msg' . $order . '
				LIMIT 1', array('current_board' => $board, 'current_member' => $user_info['id'], 'current_topic' => $topic, 'is_approved' => 1, 'id_member_started' => 0));
            // No more left.
            if ($smcFunc['db_num_rows']($request) == 0) {
                $smcFunc['db_free_result']($request);
                // Roll over - if we're going prev, get the last - otherwise the first.
                $request = $smcFunc['db_query']('', '
					SELECT id_topic
					FROM {db_prefix}topics
					WHERE id_board = {int:current_board}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
						AND (approved = {int:is_approved} OR (id_member_started != {int:id_member_started} AND id_member_started = {int:current_member}))') . '
					ORDER BY' . (empty($modSettings['enableStickyTopics']) ? '' : ' is_sticky' . $order . ',') . ' id_last_msg' . $order . '
					LIMIT 1', array('current_board' => $board, 'current_member' => $user_info['id'], 'is_approved' => 1, 'id_member_started' => 0));
            }
            // Now you can be sure $topic is the id_topic to view.
            list($topic) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
            $context['current_topic'] = $topic;
        }
        // Go to the newest message on this topic.
        $_REQUEST['start'] = 'new';
    }
    // Add 1 to the number of views of this topic.
    if (empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic) {
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}topics
			SET num_views = num_views + 1
			WHERE id_topic = {int:current_topic}', array('current_topic' => $topic));
        $_SESSION['last_read_topic'] = $topic;
    }
    // Get all the important topic info.
    $request = $smcFunc['db_query']('', '
		SELECT
			t.num_replies, t.num_views, t.locked, ms.subject, t.is_sticky, t.id_poll,
			t.id_member_started, t.id_first_msg, t.id_last_msg, t.approved, t.unapproved_posts,
			' . ($user_info['is_guest'] ? 't.id_last_msg + 1' : 'IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1') . ' AS new_from
			' . (!empty($modSettings['recycle_board']) && $modSettings['recycle_board'] == $board ? ', id_previous_board, id_previous_topic' : '') . '
		FROM {db_prefix}topics AS t
			INNER JOIN {db_prefix}messages AS ms ON (ms.id_msg = t.id_first_msg)' . ($user_info['is_guest'] ? '' : '
			LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = {int:current_topic} AND lt.id_member = {int:current_member})
			LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})') . '
		WHERE t.id_topic = {int:current_topic}
		LIMIT 1', array('current_member' => $user_info['id'], 'current_topic' => $topic, 'current_board' => $board));
    if ($smcFunc['db_num_rows']($request) == 0) {
        fatal_lang_error('not_a_topic', false);
    }
    $topicinfo = $smcFunc['db_fetch_assoc']($request);
    $smcFunc['db_free_result']($request);
    $context['real_num_replies'] = $context['num_replies'] = $topicinfo['num_replies'];
    $context['topic_first_message'] = $topicinfo['id_first_msg'];
    $context['topic_last_message'] = $topicinfo['id_last_msg'];
    // Add up unapproved replies to get real number of replies...
    if ($modSettings['postmod_active'] && allowedTo('approve_posts')) {
        $context['real_num_replies'] += $topicinfo['unapproved_posts'] - ($topicinfo['approved'] ? 0 : 1);
    }
    // If this topic has unapproved posts, we need to work out how many posts the user can see, for page indexing.
    if ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !$user_info['is_guest'] && !allowedTo('approve_posts')) {
        $request = $smcFunc['db_query']('', '
			SELECT COUNT(id_member) AS my_unapproved_posts
			FROM {db_prefix}messages
			WHERE id_topic = {int:current_topic}
				AND id_member = {int:current_member}
				AND approved = 0', array('current_topic' => $topic, 'current_member' => $user_info['id']));
        list($myUnapprovedPosts) = $smcFunc['db_fetch_row']($request);
        $smcFunc['db_free_result']($request);
        $context['total_visible_posts'] = $context['num_replies'] + $myUnapprovedPosts + ($topicinfo['approved'] ? 1 : 0);
    } else {
        $context['total_visible_posts'] = $context['num_replies'] + $topicinfo['unapproved_posts'] + ($topicinfo['approved'] ? 1 : 0);
    }
    // When was the last time this topic was replied to?  Should we warn them about it?
    $request = $smcFunc['db_query']('', '
		SELECT poster_time
		FROM {db_prefix}messages
		WHERE id_msg = {int:id_last_msg}
		LIMIT 1', array('id_last_msg' => $topicinfo['id_last_msg']));
    list($lastPostTime) = $smcFunc['db_fetch_row']($request);
    $smcFunc['db_free_result']($request);
    $context['oldTopicError'] = !empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky);
    // The start isn't a number; it's information about what to do, where to go.
    if (!is_numeric($_REQUEST['start'])) {
        // Redirect to the page and post with new messages, originally by Omar Bazavilvazo.
        if ($_REQUEST['start'] == 'new') {
            // Guests automatically go to the last post.
            if ($user_info['is_guest']) {
                $context['start_from'] = $context['total_visible_posts'] - 1;
                $_REQUEST['start'] = 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 = $smcFunc['db_query']('', '
					SELECT IFNULL(lt.id_msg, IFNULL(lmr.id_msg, -1)) + 1 AS new_from
					FROM {db_prefix}topics AS t
						LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = {int:current_topic} AND lt.id_member = {int:current_member})
						LEFT JOIN {db_prefix}log_mark_read AS lmr ON (lmr.id_board = {int:current_board} AND lmr.id_member = {int:current_member})
					WHERE t.id_topic = {int:current_topic}
					LIMIT 1', array('current_board' => $board, 'current_member' => $user_info['id'], 'current_topic' => $topic));
                list($new_from) = $smcFunc['db_fetch_row']($request);
                $smcFunc['db_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 = $smcFunc['db_query']('', '
					SELECT COUNT(*)
					FROM {db_prefix}messages
					WHERE poster_time < {int:timestamp}
						AND id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts') ? '
						AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')' : ''), array('current_topic' => $topic, 'current_member' => $user_info['id'], 'is_approved' => 1, 'timestamp' => $timestamp));
                list($context['start_from']) = $smcFunc['db_fetch_row']($request);
                $smcFunc['db_free_result']($request);
                // Handle view_newest_first options, and get the correct start value.
                $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $context['total_visible_posts'] - $context['start_from'] - 1;
            }
        } elseif (substr($_REQUEST['start'], 0, 3) == 'msg') {
            $virtual_msg = (int) substr($_REQUEST['start'], 3);
            if (!$topicinfo['unapproved_posts'] && $virtual_msg >= $topicinfo['id_last_msg']) {
                $context['start_from'] = $context['total_visible_posts'] - 1;
            } elseif (!$topicinfo['unapproved_posts'] && $virtual_msg <= $topicinfo['id_first_msg']) {
                $context['start_from'] = 0;
            } else {
                // Find the start value for that message......
                $request = $smcFunc['db_query']('', '
					SELECT COUNT(*)
					FROM {db_prefix}messages
					WHERE id_msg < {int:virtual_msg}
						AND id_topic = {int:current_topic}' . ($modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts') ? '
						AND (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')' : ''), array('current_member' => $user_info['id'], 'current_topic' => $topic, 'virtual_msg' => $virtual_msg, 'is_approved' => 1, 'no_member' => 0));
                list($context['start_from']) = $smcFunc['db_fetch_row']($request);
                $smcFunc['db_free_result']($request);
            }
            // We need to reverse the start as well in this case.
            $_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $context['total_visible_posts'] - $context['start_from'] - 1;
        }
    }
    // 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');
    // Do we need to show the visual verification image?
    $context['require_verification'] = !$user_info['is_mod'] && !$user_info['is_admin'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || $user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1);
    if ($context['require_verification']) {
        require_once $sourcedir . '/Subs-Editor.php';
        $verificationOptions = array('id' => 'post');
        $context['require_verification'] = create_control_verification($verificationOptions);
        $context['visual_verification_id'] = $verificationOptions['id'];
    }
    // Are we showing signatures - or disabled fields?
    $context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1;
    $context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
    // Censor the title...
    censorText($topicinfo['subject']);
    $context['page_title'] = $topicinfo['subject'];
    // Is this already an article?
    $request = $smcFunc['db_query']('', '
		SELECT id_message
		FROM {db_prefix}sp_articles
		WHERE id_message = {int:message}', array('message' => $context['topic_first_message']));
    list($context['topic_is_article']) = $smcFunc['db_fetch_row']($request);
    $smcFunc['db_free_result']($request);
    // Is this topic sticky, or can it even be?
    $topicinfo['is_sticky'] = empty($modSettings['enableStickyTopics']) ? '0' : $topicinfo['is_sticky'];
    // Default this topic to not marked for notifications... of course...
    $context['is_marked_notify'] = false;
    // Did we report a post to a moderator just now?
    $context['report_sent'] = isset($_GET['reportsent']);
    // 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 = $smcFunc['db_query']('', '
			SELECT
				lo.id_member, lo.log_time, mem.real_name, mem.member_name, mem.show_online,
				mg.online_color, mg.id_group, mg.group_name
			FROM {db_prefix}log_online AS lo
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lo.id_member)
				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:reg_id_group} THEN mem.id_post_group ELSE mem.id_group END)
			WHERE INSTR(lo.url, {string:in_url_string}) > 0 OR lo.session = {string:session}', array('reg_id_group' => 0, 'in_url_string' => 's:5:"topic";i:' . $topic . ';', 'session' => $user_info['is_guest'] ? 'ip' . $user_info['ip'] : session_id()));
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            if (empty($row['id_member'])) {
                continue;
            }
            if (!empty($row['online_color'])) {
                $link = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '" style="color: ' . $row['online_color'] . ';">' . $row['real_name'] . '</a>';
            } else {
                $link = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['real_name'] . '</a>';
            }
            $is_buddy = in_array($row['id_member'], $user_info['buddies']);
            if ($is_buddy) {
                $link = '<strong>' . $link . '</strong>';
            }
            // Add them both to the list and to the more detailed list.
            if (!empty($row['show_online']) || allowedTo('moderate_forum')) {
                $context['view_members_list'][$row['log_time'] . $row['member_name']] = empty($row['show_online']) ? '<em>' . $link . '</em>' : $link;
            }
            $context['view_members'][$row['log_time'] . $row['member_name']] = array('id' => $row['id_member'], 'username' => $row['member_name'], 'name' => $row['real_name'], 'group' => $row['id_group'], 'href' => $scripturl . '?action=profile;u=' . $row['id_member'], 'link' => $link, 'is_buddy' => $is_buddy, 'hidden' => empty($row['show_online']));
            if (empty($row['show_online'])) {
                $context['view_num_hidden']++;
            }
        }
        // The number of guests is equal to the rows minus the ones we actually used ;).
        $context['view_num_guests'] = $smcFunc['db_num_rows']($request) - count($context['view_members']);
        $smcFunc['db_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.
    $can_show_all = !empty($modSettings['enableAllMessages']) && $context['total_visible_posts'] > $context['messages_per_page'] && $context['total_visible_posts'] < $modSettings['enableAllMessages'];
    if (isset($_REQUEST['all']) && !$can_show_all) {
        unset($_REQUEST['all']);
    } elseif (isset($_REQUEST['all'])) {
        $_REQUEST['start'] = -1;
    }
    // Construct the page index, allowing for the .START method...
    $context['page_index'] = constructPageIndex($scripturl . '?topic=' . $topic . '.%1$d', $_REQUEST['start'], $context['total_visible_posts'], $context['messages_per_page'], true);
    $context['start'] = $_REQUEST['start'];
    // This is information about which page is current, and which page we're on - in case you don't like the constructed page index. (again, wireles..)
    $context['page_info'] = array('current_page' => $_REQUEST['start'] / $context['messages_per_page'] + 1, 'num_pages' => floor(($context['total_visible_posts'] - 1) / $context['messages_per_page']) + 1);
    // Figure out all the link to the next/prev/first/last/etc. for wireless mainly.
    $context['links'] = array('first' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.0' : '', 'prev' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] - $context['messages_per_page']) : '', 'next' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] + $context['messages_per_page']) : '', 'last' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic . '.' . floor($context['total_visible_posts'] / $context['messages_per_page']) * $context['messages_per_page'] : '', 'up' => $scripturl . '?board=' . $board . '.0');
    // If they are viewing all the posts, show all the posts, otherwise limit the number.
    if ($can_show_all) {
        if (isset($_REQUEST['all'])) {
            // No limit! (actually, there is a limit, but...)
            $context['messages_per_page'] = -1;
            $context['page_index'] .= empty($modSettings['compactTopicPagesEnable']) ? '<strong>' . $txt['all'] . '</strong> ' : '[<strong>' . $txt['all'] . '</strong>] ';
            // Set start back to 0...
            $_REQUEST['start'] = 0;
        } else {
            $context['page_index'] .= '&nbsp;<a href="' . $scripturl . '?topic=' . $topic . '.0;all">' . $txt['all'] . '</a> ';
        }
    }
    // Build the link tree.
    $context['linktree'][] = array('url' => $scripturl . '?topic=' . $topic . '.0', 'name' => $topicinfo['subject'], 'extra_before' => $settings['linktree_inline'] ? $txt['topic'] . ': ' : '');
    // 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['board_moderator'] . '">' . $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['moderator'] : $txt['moderators']) . ': ' . implode(', ', $context['link_moderators']) . ')';
    }
    // Information about the current topic...
    $context['is_locked'] = $topicinfo['locked'];
    $context['is_sticky'] = $topicinfo['is_sticky'];
    $context['is_very_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicVeryPosts'];
    $context['is_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicPosts'];
    $context['is_approved'] = $topicinfo['approved'];
    // 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'] = $user_info['id'] == $topicinfo['id_member_started'] && !$user_info['is_guest'];
    $context['topic_starter_id'] = $topicinfo['id_member_started'];
    // Set the topic's information for the template.
    $context['subject'] = $topicinfo['subject'];
    $context['num_views'] = $topicinfo['num_views'];
    $context['mark_unread_time'] = $topicinfo['new_from'];
    // Set a canonical URL for this page.
    $context['canonical_url'] = $scripturl . '?topic=' . $topic . '.' . $context['start'];
    // For quick reply we need a response prefix in the default forum language.
    if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix', 600))) {
        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 = $smcFunc['db_query']('', '
			SELECT cal.id_event, cal.start_date, cal.end_date, cal.title, cal.id_member, mem.real_name
			FROM {db_prefix}calendar AS cal
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = cal.id_member)
			WHERE cal.id_topic = {int:current_topic}
			ORDER BY start_date', array('current_topic' => $topic));
        $context['linked_calendar_events'] = array();
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            // Prepare the dates for being formatted.
            $start_date = sscanf($row['start_date'], '%04d-%02d-%02d');
            $start_date = mktime(12, 0, 0, $start_date[1], $start_date[2], $start_date[0]);
            $end_date = sscanf($row['end_date'], '%04d-%02d-%02d');
            $end_date = mktime(12, 0, 0, $end_date[1], $end_date[2], $end_date[0]);
            $context['linked_calendar_events'][] = array('id' => $row['id_event'], 'title' => $row['title'], 'can_edit' => allowedTo('calendar_edit_any') || $row['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own'), 'modify_href' => $scripturl . '?action=post;msg=' . $topicinfo['id_first_msg'] . ';topic=' . $topic . '.0;calendar;eventid=' . $row['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'start_date' => timeformat($start_date, $date_string, 'none'), 'start_timestamp' => $start_date, 'end_date' => timeformat($end_date, $date_string, 'none'), 'end_timestamp' => $end_date, 'is_last' => false);
        }
        $smcFunc['db_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 = $smcFunc['db_query']('', '
			SELECT
				p.question, p.voting_locked, p.hide_results, p.expire_time, p.max_votes, p.change_vote,
				p.guest_vote, p.id_member, IFNULL(mem.real_name, p.poster_name) AS poster_name, p.num_guest_voters, p.reset_poll
			FROM {db_prefix}polls AS p
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = p.id_member)
			WHERE p.id_poll = {int:id_poll}
			LIMIT 1', array('id_poll' => $topicinfo['id_poll']));
        $pollinfo = $smcFunc['db_fetch_assoc']($request);
        $smcFunc['db_free_result']($request);
        $request = $smcFunc['db_query']('', '
			SELECT COUNT(DISTINCT id_member) AS total
			FROM {db_prefix}log_polls
			WHERE id_poll = {int:id_poll}
				AND id_member != {int:not_guest}', array('id_poll' => $topicinfo['id_poll'], 'not_guest' => 0));
        list($pollinfo['total']) = $smcFunc['db_fetch_row']($request);
        $smcFunc['db_free_result']($request);
        // Total voters needs to include guest voters
        $pollinfo['total'] += $pollinfo['num_guest_voters'];
        // Get all the options, and calculate the total votes.
        $request = $smcFunc['db_query']('', '
			SELECT pc.id_choice, pc.label, pc.votes, IFNULL(lp.id_choice, -1) AS voted_this
			FROM {db_prefix}poll_choices AS pc
				LEFT JOIN {db_prefix}log_polls AS lp ON (lp.id_choice = pc.id_choice AND lp.id_poll = {int:id_poll} AND lp.id_member = {int:current_member} AND lp.id_member != {int:not_guest})
			WHERE pc.id_poll = {int:id_poll}', array('current_member' => $user_info['id'], 'id_poll' => $topicinfo['id_poll'], 'not_guest' => 0));
        $pollOptions = array();
        $realtotal = 0;
        $pollinfo['has_voted'] = false;
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            censorText($row['label']);
            $pollOptions[$row['id_choice']] = $row;
            $realtotal += $row['votes'];
            $pollinfo['has_voted'] |= $row['voted_this'] != -1;
        }
        $smcFunc['db_free_result']($request);
        // If this is a guest we need to do our best to work out if they have voted, and what they voted for.
        if ($user_info['is_guest'] && $pollinfo['guest_vote'] && allowedTo('poll_vote')) {
            if (!empty($_COOKIE['guest_poll_vote']) && preg_match('~^[0-9,;]+$~', $_COOKIE['guest_poll_vote']) && strpos($_COOKIE['guest_poll_vote'], ';' . $topicinfo['id_poll'] . ',') !== false) {
                // ;id,timestamp,[vote,vote...]; etc
                $guestinfo = explode(';', $_COOKIE['guest_poll_vote']);
                // Find the poll we're after.
                foreach ($guestinfo as $i => $guestvoted) {
                    $guestvoted = explode(',', $guestvoted);
                    if ($guestvoted[0] == $topicinfo['id_poll']) {
                        break;
                    }
                }
                // Has the poll been reset since guest voted?
                if ($pollinfo['reset_poll'] > $guestvoted[1]) {
                    // Remove the poll info from the cookie to allow guest to vote again
                    unset($guestinfo[$i]);
                    if (!empty($guestinfo)) {
                        $_COOKIE['guest_poll_vote'] = ';' . implode(';', $guestinfo);
                    } else {
                        unset($_COOKIE['guest_poll_vote']);
                    }
                } else {
                    // What did they vote for?
                    unset($guestvoted[0], $guestvoted[1]);
                    foreach ($pollOptions as $choice => $details) {
                        $pollOptions[$choice]['voted_this'] = in_array($choice, $guestvoted) ? 1 : -1;
                        $pollinfo['has_voted'] |= $pollOptions[$choice]['voted_this'] != -1;
                    }
                    unset($choice, $details, $guestvoted);
                }
                unset($guestinfo, $guestvoted, $i);
            }
        }
        // Set up the basic poll information.
        $context['poll'] = array('id' => $topicinfo['id_poll'], 'image' => 'normal_' . (empty($pollinfo['voting_locked']) ? 'poll' : 'locked_poll'), 'question' => parse_bbc($pollinfo['question']), 'total_votes' => $pollinfo['total'], 'change_vote' => !empty($pollinfo['change_vote']), 'is_locked' => !empty($pollinfo['voting_locked']), '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['max_votes'] > 1 ? sprintf($txt['poll_options6'], min(count($pollOptions), $pollinfo['max_votes'])) : '', 'is_expired' => !empty($pollinfo['expire_time']) && $pollinfo['expire_time'] < time(), 'expire_time' => !empty($pollinfo['expire_time']) ? timeformat($pollinfo['expire_time']) : 0, 'has_voted' => !empty($pollinfo['has_voted']), 'starter' => array('id' => $pollinfo['id_member'], 'name' => $row['poster_name'], 'href' => $pollinfo['id_member'] == 0 ? '' : $scripturl . '?action=profile;u=' . $pollinfo['id_member'], 'link' => $pollinfo['id_member'] == 0 ? $row['poster_name'] : '<a href="' . $scripturl . '?action=profile;u=' . $pollinfo['id_member'] . '">' . $row['poster_name'] . '</a>'));
        // Make the lock and edit permissions defined above more directly accessible.
        $context['allow_lock_poll'] = $context['poll']['lock'];
        $context['allow_edit_poll'] = $context['poll']['edit'];
        // You're allowed to vote if:
        // 1. the poll did not expire, and
        // 2. you're either not a guest OR guest voting is enabled... 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'] || $pollinfo['guest_vote'] && allowedTo('poll_vote')) && empty($pollinfo['voting_locked']) && 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 (hide_results == 0), or
        // 3. you can see them after you voted (hide_results == 1), or
        // 4. you've waited long enough for the poll to expire. (whether hide_results is 1 or 2.)
        $context['allow_poll_view'] = allowedTo('moderate_board') || $pollinfo['hide_results'] == 0 || $pollinfo['hide_results'] == 1 && $context['poll']['has_voted'] || $context['poll']['is_expired'];
        $context['poll']['show_results'] = $context['allow_poll_view'] && (isset($_REQUEST['viewresults']) || isset($_REQUEST['viewResults']));
        $context['show_view_results_button'] = $context['allow_vote'] && (!$context['allow_poll_view'] || !$context['poll']['show_results'] || !$context['poll']['has_voted']);
        // 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['voting_locked']) && allowedTo('poll_vote') && $context['poll']['has_voted'] && $context['poll']['change_vote'];
        // You're allowed to return to voting options if:
        // 1. you are (still) allowed to vote.
        // 2. you are currently seeing the results.
        $context['allow_return_vote'] = $context['allow_vote'] && $context['poll']['show_results'];
        // 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['voted_this'] != -1, 'bar' => '<span style="white-space: nowrap;"><img src="' . $settings['images_url'] . '/poll_' . ($context['right_to_left'] ? 'right' : 'left') . '.gif" alt="" /><img src="' . $settings['images_url'] . '/poll_middle.gif" width="' . $barWide . '" height="12" alt="-" /><img src="' . $settings['images_url'] . '/poll_' . ($context['right_to_left'] ? 'left' : 'right') . '.gif" alt="" /></span>', 'bar_ndt' => $bar > 0 ? '<div class="bar" style="width: ' . ($bar * 3.5 + 4) . 'px;"><div style="width: ' . $bar * 3.5 . 'px;"></div></div>' : '', 'bar_width' => $barWide, 'option' => parse_bbc($option['label']), 'vote_button' => '<input type="' . ($pollinfo['max_votes'] > 1 ? 'checkbox' : 'radio') . '" name="options[]" id="options-' . $i . '" value="' . $i . '" class="input_' . ($pollinfo['max_votes'] > 1 ? 'check' : 'radio') . '" />');
        }
    }
    // Calculate the fastest way to get the messages!
    $ascending = empty($options['view_newest_first']);
    $start = $_REQUEST['start'];
    $limit = $context['messages_per_page'];
    $firstIndex = 0;
    if ($start >= $context['total_visible_posts'] / 2 && $context['messages_per_page'] != -1) {
        $ascending = !$ascending;
        $limit = $context['total_visible_posts'] <= $start + $limit ? $context['total_visible_posts'] - $start : $limit;
        $start = $context['total_visible_posts'] <= $start + $limit ? 0 : $context['total_visible_posts'] - $start - $limit;
        $firstIndex = $limit - 1;
    }
    // Get each post and poster in this topic.
    $request = $smcFunc['db_query']('display_get_post_poster', '
		SELECT id_msg, id_member, approved
		FROM {db_prefix}messages
		WHERE id_topic = {int:current_topic}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : (!empty($modSettings['db_mysql_group_by_fix']) ? '' : '
		GROUP BY id_msg') . '
		HAVING (approved = {int:is_approved}' . ($user_info['is_guest'] ? '' : ' OR id_member = {int:current_member}') . ')') . '
		ORDER BY id_msg ' . ($ascending ? '' : 'DESC') . ($context['messages_per_page'] == -1 ? '' : '
		LIMIT ' . $start . ', ' . $limit), array('current_member' => $user_info['id'], 'current_topic' => $topic, 'is_approved' => 1, 'blank_id_member' => 0));
    $messages = array();
    $all_posters = array();
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        if (!empty($row['id_member'])) {
            $all_posters[$row['id_msg']] = $row['id_member'];
        }
        $messages[] = $row['id_msg'];
    }
    $smcFunc['db_free_result']($request);
    $posters = array_unique($all_posters);
    // Guests can't mark topics read or for notifications, just can't sorry.
    if (!$user_info['is_guest']) {
        $mark_at_msg = max($messages);
        if ($mark_at_msg >= $topicinfo['id_last_msg']) {
            $mark_at_msg = $modSettings['maxMsgID'];
        }
        if ($mark_at_msg >= $topicinfo['new_from']) {
            $smcFunc['db_insert']($topicinfo['new_from'] == 0 ? 'ignore' : 'replace', '{db_prefix}log_topics', array('id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int'), array($user_info['id'], $topic, $mark_at_msg), array('id_member', 'id_topic'));
        }
        // Check for notifications on this topic OR board.
        $request = $smcFunc['db_query']('', '
			SELECT sent, id_topic
			FROM {db_prefix}log_notify
			WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board})
				AND id_member = {int:current_member}
			LIMIT 2', array('current_board' => $board, 'current_member' => $user_info['id'], 'current_topic' => $topic));
        $do_once = true;
        while ($row = $smcFunc['db_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) {
                $smcFunc['db_query']('', '
					UPDATE {db_prefix}log_notify
					SET sent = {int:is_not_sent}
					WHERE (id_topic = {int:current_topic} OR id_board = {int:current_board})
						AND id_member = {int:current_member}', array('current_board' => $board, 'current_member' => $user_info['id'], 'current_topic' => $topic, 'is_not_sent' => 0));
                $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 = $smcFunc['db_query']('', '
				SELECT COUNT(*)
				FROM {db_prefix}topics AS t
					LEFT JOIN {db_prefix}log_boards AS lb ON (lb.id_board = {int:current_board} AND lb.id_member = {int:current_member})
					LEFT JOIN {db_prefix}log_topics AS lt ON (lt.id_topic = t.id_topic AND lt.id_member = {int:current_member})
				WHERE t.id_board = {int:current_board}
					AND t.id_last_msg > IFNULL(lb.id_msg, 0)
					AND t.id_last_msg > IFNULL(lt.id_msg, 0)' . (empty($_SESSION['id_msg_last_visit']) ? '' : '
					AND t.id_last_msg > {int:id_msg_last_visit}'), array('current_board' => $board, 'current_member' => $user_info['id'], 'id_msg_last_visit' => (int) $_SESSION['id_msg_last_visit']));
            list($numNewTopics) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_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'])) {
            $smcFunc['db_insert']('replace', '{db_prefix}log_boards', array('id_msg' => 'int', 'id_member' => 'int', 'id_board' => 'int'), array($modSettings['maxMsgID'], $user_info['id'], $board), array('id_member', 'id_board'));
        }
    }
    $attachments = array();
    // If there _are_ messages here... (probably an error otherwise :!)
    if (!empty($messages)) {
        // Fetch attachments.
        if (!empty($modSettings['attachmentEnable']) && allowedTo('view_attachments')) {
            $request = $smcFunc['db_query']('', '
				SELECT
					a.id_attach, a.id_folder, a.id_msg, a.filename, a.file_hash, IFNULL(a.size, 0) AS filesize, a.downloads, a.approved,
					a.width, a.height' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : ',
					IFNULL(thumb.id_attach, 0) AS id_thumb, thumb.width AS thumb_width, thumb.height AS thumb_height') . '
				FROM {db_prefix}attachments AS a' . (empty($modSettings['attachmentShowImages']) || empty($modSettings['attachmentThumbnails']) ? '' : '
					LEFT JOIN {db_prefix}attachments AS thumb ON (thumb.id_attach = a.id_thumb)') . '
				WHERE a.id_msg IN ({array_int:message_list})
					AND a.attachment_type = {int:attachment_type}', array('message_list' => $messages, 'attachment_type' => 0, 'is_approved' => 1));
            $temp = array();
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                if (!$row['approved'] && $modSettings['postmod_active'] && !allowedTo('approve_posts') && (!isset($all_posters[$row['id_msg']]) || $all_posters[$row['id_msg']] != $user_info['id'])) {
                    continue;
                }
                $temp[$row['id_attach']] = $row;
                if (!isset($attachments[$row['id_msg']])) {
                    $attachments[$row['id_msg']] = array();
                }
            }
            $smcFunc['db_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 = $smcFunc['db_query']('', '
			SELECT
				id_msg, icon, subject, poster_time, poster_ip, id_member, modified_time, modified_name, body,
				smileys_enabled, poster_name, poster_email, approved,
				id_msg_modified < {int:new_from} AS is_read
			FROM {db_prefix}messages
			WHERE id_msg IN ({array_int:message_list})
			ORDER BY id_msg' . (empty($options['view_newest_first']) ? '' : ' DESC'), array('message_list' => $messages, 'new_from' => $topicinfo['new_from']));
        // Go to the last message if the given time is beyond the time of the last message.
        if (isset($context['start_from']) && $context['start_from'] >= $topicinfo['num_replies']) {
            $context['start_from'] = $topicinfo['num_replies'];
        }
        // Since the anchor information is needed on the top of the page we load these variables beforehand.
        $context['first_message'] = isset($messages[$firstIndex]) ? $messages[$firstIndex] : $messages[0];
        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['num_replies'] - $context['start_from'];
        }
    } else {
        $messages_request = false;
        $context['first_message'] = 0;
        $context['first_new_message'] = false;
    }
    $context['jump_to'] = array('label' => addslashes(un_htmlspecialchars($txt['jump_to'])), 'board_name' => htmlspecialchars(strtr(strip_tags($board_info['name']), array('&amp;' => '&'))), 'child_level' => $board_info['child_level']);
    // Set the callback.  (do you REALIZE how much memory all the messages would take?!?)
    $context['get_message'] = 'prepareDisplayContext';
    // Now set all the wonderful, wonderful permissions... like moderation ones...
    $common_permissions = array('can_approve' => 'approve_posts', 'can_ban' => 'manage_bans', 'can_sticky' => 'make_sticky', 'can_merge' => 'merge_any', 'can_split' => 'split_any', 'calendar_post' => 'calendar_post', 'can_mark_notify' => 'mark_any_notify', 'can_send_topic' => 'send_topic', 'can_send_pm' => 'pm_send', 'can_report_moderator' => 'report_any', 'can_moderate_forum' => 'moderate_forum', 'can_issue_warning' => 'issue_warning', 'can_restore_topic' => 'move_any', 'can_restore_msg' => 'move_any');
    foreach ($common_permissions as $contextual => $perm) {
        $context[$contextual] = allowedTo($perm);
    }
    // Permissions with _any/_own versions.  $context[YYY] => ZZZ_any/_own.
    $anyown_permissions = array('can_move' => 'move', 'can_lock' => 'lock', 'can_delete' => 'remove', 'can_add_poll' => 'poll_add', 'can_remove_poll' => 'poll_remove', 'can_reply' => 'post_reply', 'can_reply_unapproved' => 'post_unapproved_replies');
    foreach ($anyown_permissions as $contextual => $perm) {
        $context[$contextual] = allowedTo($perm . '_any') || $context['user']['started'] && allowedTo($perm . '_own');
    }
    // Cleanup all the permissions with extra stuff...
    $context['can_mark_notify'] &= !$context['user']['is_guest'];
    $context['can_sticky'] &= !empty($modSettings['enableStickyTopics']);
    $context['calendar_post'] &= !empty($modSettings['cal_enabled']);
    $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');
    $context['can_reply_unapproved'] &= $modSettings['postmod_active'] && (empty($topicinfo['locked']) || allowedTo('moderate_board'));
    $context['can_issue_warning'] &= in_array('w', $context['admin_features']) && $modSettings['warning_settings'][0] == 1;
    // Handle approval flags...
    $context['can_reply_approved'] = $context['can_reply'];
    $context['can_reply'] |= $context['can_reply_unapproved'];
    $context['can_quote'] = $context['can_reply'] && (empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])));
    $context['can_mark_unread'] = !$user_info['is_guest'] && $settings['show_mark_read'];
    $context['can_send_topic'] = (!$modSettings['postmod_active'] || $topicinfo['approved']) && allowedTo('send_topic');
    // Start this off for quick moderation - it will be or'd for each post.
    $context['can_remove_post'] = allowedTo('delete_any') || allowedTo('delete_replies') && $context['user']['started'];
    // Can restore topic?  That's if the topic is in the recycle board and has a previous restore state.
    $context['can_restore_topic'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_board']);
    $context['can_restore_msg'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_topic']);
    // Wireless shows a "more" if you can do anything special.
    if (WIRELESS && WIRELESS_PROTOCOL != 'wap') {
        $context['wireless_more'] = $context['can_sticky'] || $context['can_lock'] || allowedTo('modify_any');
        $context['wireless_moderate'] = isset($_GET['moderate']) ? ';moderate' : '';
    }
    // Load up the "double post" sequencing magic.
    if (!empty($options['display_quick_reply'])) {
        checkSubmitOnce('register');
        $context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
        $context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
    }
}
Beispiel #18
0
function Post()
{
    global $txt, $scripturl, $topic, $modSettings, $board;
    global $user_info, $sc, $board_info, $context, $settings;
    global $sourcedir, $options, $smcFunc, $language;
    loadLanguage('Post');
    // You can't reply with a poll... hacker.
    if (isset($_REQUEST['poll']) && !empty($topic) && !isset($_REQUEST['msg'])) {
        unset($_REQUEST['poll']);
    }
    // Posting an event?
    $context['make_event'] = isset($_REQUEST['calendar']);
    $context['robot_no_index'] = true;
    // You must be posting to *some* board.
    if (empty($board) && !$context['make_event']) {
        fatal_lang_error('no_board', false);
    }
    require_once 'include/Subs-Post.php';
    if (isset($_REQUEST['xml'])) {
        $context['sub_template'] = 'post';
        // Just in case of an earlier error...
        $context['preview_message'] = '';
        $context['preview_subject'] = '';
    }
    // No message is complete without a topic.
    if (empty($topic) && !empty($_REQUEST['msg'])) {
        $request = $smcFunc['db_query']('', '
			SELECT id_topic
			FROM {db_prefix}messages
			WHERE id_msg = {int:msg}', array('msg' => (int) $_REQUEST['msg']));
        if ($smcFunc['db_num_rows']($request) != 1) {
            unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']);
        } else {
            list($topic) = $smcFunc['db_fetch_row']($request);
        }
        $smcFunc['db_free_result']($request);
    }
    // Check if it's locked.  It isn't locked if no topic is specified.
    if (!empty($topic)) {
        $request = $smcFunc['db_query']('', '
			SELECT
				t.locked, IFNULL(ln.id_topic, 0) AS notify, t.is_sticky, t.id_poll, t.id_last_msg, mf.id_member,
				t.id_first_msg, mf.subject,
				CASE WHEN ml.poster_time > ml.modified_time THEN ml.poster_time ELSE ml.modified_time END AS last_post_time
			FROM {db_prefix}topics AS t
				LEFT JOIN {db_prefix}log_notify AS ln ON (ln.id_topic = t.id_topic AND ln.id_member = {int:current_member})
				LEFT JOIN {db_prefix}messages AS mf ON (mf.id_msg = t.id_first_msg)
				LEFT JOIN {db_prefix}messages AS ml ON (ml.id_msg = t.id_last_msg)
			WHERE t.id_topic = {int:current_topic}
			LIMIT 1', array('current_member' => $user_info['id'], 'current_topic' => $topic));
        list($locked, $context['notify'], $sticky, $pollID, $context['topic_last_message'], $id_member_poster, $id_first_msg, $first_subject, $lastPostTime) = $smcFunc['db_fetch_row']($request);
        $smcFunc['db_free_result']($request);
        // If this topic already has a poll, they sure can't add another.
        if (isset($_REQUEST['poll']) && $pollID > 0) {
            unset($_REQUEST['poll']);
        }
        if (empty($_REQUEST['msg'])) {
            if ($user_info['is_guest'] && !allowedTo('post_reply_any') && (!$modSettings['postmod_active'] || !allowedTo('post_unapproved_replies_any'))) {
                is_not_guest();
            }
            // By default the reply will be approved...
            $context['becomes_approved'] = true;
            if ($id_member_poster != $user_info['id']) {
                if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any')) {
                    $context['becomes_approved'] = false;
                } else {
                    isAllowedTo('post_reply_any');
                }
            } elseif (!allowedTo('post_reply_any')) {
                if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own')) {
                    $context['becomes_approved'] = false;
                } else {
                    isAllowedTo('post_reply_own');
                }
            }
        } else {
            $context['becomes_approved'] = true;
        }
        $context['can_lock'] = allowedTo('lock_any') || $user_info['id'] == $id_member_poster && allowedTo('lock_own');
        $context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']);
        $context['notify'] = !empty($context['notify']);
        $context['sticky'] = isset($_REQUEST['sticky']) ? !empty($_REQUEST['sticky']) : $sticky;
    } else {
        $context['becomes_approved'] = true;
        if (!$context['make_event'] || !empty($board)) {
            if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics')) {
                $context['becomes_approved'] = false;
            } else {
                isAllowedTo('post_new');
            }
        }
        $locked = 0;
        // !!! These won't work if you're making an event.
        $context['can_lock'] = allowedTo(array('lock_any', 'lock_own'));
        $context['can_sticky'] = allowedTo('make_sticky') && !empty($modSettings['enableStickyTopics']);
        $context['notify'] = !empty($context['notify']);
        $context['sticky'] = !empty($_REQUEST['sticky']);
    }
    // !!! These won't work if you're posting an event!
    $context['can_notify'] = allowedTo('mark_any_notify');
    $context['can_move'] = allowedTo('move_any');
    $context['move'] = !empty($_REQUEST['move']);
    $context['announce'] = !empty($_REQUEST['announce']);
    // You can only announce topics that will get approved...
    $context['can_announce'] = allowedTo('announce_topic') && $context['becomes_approved'];
    $context['locked'] = !empty($locked) || !empty($_REQUEST['lock']);
    $context['can_quote'] = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']));
    // Generally don't show the approval box... (Assume we want things approved)
    $context['show_approval'] = false;
    // An array to hold all the attachments for this topic.
    $context['current_attachments'] = array();
    // Don't allow a post if it's locked and you aren't all powerful.
    if ($locked && !allowedTo('moderate_board')) {
        fatal_lang_error('topic_locked', false);
    }
    // Check the users permissions - is the user allowed to add or post a poll?
    if (isset($_REQUEST['poll']) && $modSettings['pollMode'] == '1') {
        // New topic, new poll.
        if (empty($topic)) {
            isAllowedTo('poll_post');
        } elseif ($user_info['id'] == $id_member_poster && !allowedTo('poll_add_any')) {
            isAllowedTo('poll_add_own');
        } else {
            isAllowedTo('poll_add_any');
        }
        require_once $sourcedir . '/Subs-Members.php';
        $allowedVoteGroups = groupsAllowedTo('poll_vote', $board);
        // Set up the poll options.
        $context['poll_options'] = array('max_votes' => empty($_POST['poll_max_votes']) ? '1' : max(1, $_POST['poll_max_votes']), 'hide' => empty($_POST['poll_hide']) ? 0 : $_POST['poll_hide'], 'expire' => !isset($_POST['poll_expire']) ? '' : $_POST['poll_expire'], 'change_vote' => isset($_POST['poll_change_vote']), 'guest_vote' => isset($_POST['poll_guest_vote']), 'guest_vote_enabled' => in_array(-1, $allowedVoteGroups['allowed']));
        // Make all five poll choices empty.
        $context['choices'] = array(array('id' => 0, 'number' => 1, 'label' => '', 'is_last' => false), array('id' => 1, 'number' => 2, 'label' => '', 'is_last' => false), array('id' => 2, 'number' => 3, 'label' => '', 'is_last' => false), array('id' => 3, 'number' => 4, 'label' => '', 'is_last' => false), array('id' => 4, 'number' => 5, 'label' => '', 'is_last' => true));
    }
    if ($context['make_event']) {
        // They might want to pick a board.
        if (!isset($context['current_board'])) {
            $context['current_board'] = 0;
        }
        // Start loading up the event info.
        $context['event'] = array();
        $context['event']['title'] = isset($_REQUEST['evtitle']) ? htmlspecialchars(stripslashes($_REQUEST['evtitle'])) : '';
        $context['event']['id'] = isset($_REQUEST['eventid']) ? (int) $_REQUEST['eventid'] : -1;
        $context['event']['new'] = $context['event']['id'] == -1;
        // Permissions check!
        isAllowedTo('calendar_post');
        // Editing an event?  (but NOT previewing!?)
        if (!$context['event']['new'] && !isset($_REQUEST['subject'])) {
            // If the user doesn't have permission to edit the post in this topic, redirect them.
            if ((empty($id_member_poster) || $id_member_poster != $user_info['id'] || !allowedTo('modify_own')) && !allowedTo('modify_any')) {
                require_once $sourcedir . '/Calendar.php';
                return CalendarPost();
            }
            // Get the current event information.
            $request = $smcFunc['db_query']('', '
				SELECT
					id_member, title, MONTH(start_date) AS month, DAYOFMONTH(start_date) AS day,
					YEAR(start_date) AS year, (TO_DAYS(end_date) - TO_DAYS(start_date)) AS span
				FROM {db_prefix}calendar
				WHERE id_event = {int:id_event}
				LIMIT 1', array('id_event' => $context['event']['id']));
            $row = $smcFunc['db_fetch_assoc']($request);
            $smcFunc['db_free_result']($request);
            // Make sure the user is allowed to edit this event.
            if ($row['id_member'] != $user_info['id']) {
                isAllowedTo('calendar_edit_any');
            } elseif (!allowedTo('calendar_edit_any')) {
                isAllowedTo('calendar_edit_own');
            }
            $context['event']['month'] = $row['month'];
            $context['event']['day'] = $row['day'];
            $context['event']['year'] = $row['year'];
            $context['event']['title'] = $row['title'];
            $context['event']['span'] = $row['span'] + 1;
        } else {
            $today = getdate();
            // You must have a month and year specified!
            if (!isset($_REQUEST['month'])) {
                $_REQUEST['month'] = $today['mon'];
            }
            if (!isset($_REQUEST['year'])) {
                $_REQUEST['year'] = $today['year'];
            }
            $context['event']['month'] = (int) $_REQUEST['month'];
            $context['event']['year'] = (int) $_REQUEST['year'];
            $context['event']['day'] = isset($_REQUEST['day']) ? $_REQUEST['day'] : ($_REQUEST['month'] == $today['mon'] ? $today['mday'] : 0);
            $context['event']['span'] = isset($_REQUEST['span']) ? $_REQUEST['span'] : 1;
            // Make sure the year and month are in the valid range.
            if ($context['event']['month'] < 1 || $context['event']['month'] > 12) {
                fatal_lang_error('invalid_month', false);
            }
            if ($context['event']['year'] < $modSettings['cal_minyear'] || $context['event']['year'] > $modSettings['cal_maxyear']) {
                fatal_lang_error('invalid_year', false);
            }
            // Get a list of boards they can post in.
            $boards = boardsAllowedTo('post_new');
            if (empty($boards)) {
                fatal_lang_error('cannot_post_new', 'user');
            }
            // Load a list of boards for this event in the context.
            require_once 'include/Subs-MessageIndex.php';
            $boardListOptions = array('included_boards' => in_array(0, $boards) ? null : $boards, 'not_redirection' => true, 'use_permissions' => true, 'selected_board' => empty($context['current_board']) ? $modSettings['cal_defaultboard'] : $context['current_board']);
            $context['event']['categories'] = getBoardList($boardListOptions);
        }
        // Find the last day of the month.
        $context['event']['last_day'] = (int) strftime('%d', mktime(0, 0, 0, $context['event']['month'] == 12 ? 1 : $context['event']['month'] + 1, 0, $context['event']['month'] == 12 ? $context['event']['year'] + 1 : $context['event']['year']));
        $context['event']['board'] = !empty($board) ? $board : $modSettings['cal_defaultboard'];
    }
    if (empty($context['post_errors'])) {
        $context['post_errors'] = array();
    }
    // See if any new replies have come along.
    if (empty($_REQUEST['msg']) && !empty($topic)) {
        if (empty($options['no_new_reply_warning']) && isset($_REQUEST['last_msg']) && $context['topic_last_message'] > $_REQUEST['last_msg']) {
            $request = $smcFunc['db_query']('', '
				SELECT COUNT(*)
				FROM {db_prefix}messages
				WHERE id_topic = {int:current_topic}
					AND id_msg > {int:last_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
					AND approved = {int:approved}') . '
				LIMIT 1', array('current_topic' => $topic, 'last_msg' => (int) $_REQUEST['last_msg'], 'approved' => 1));
            list($context['new_replies']) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
            if (!empty($context['new_replies'])) {
                if ($context['new_replies'] == 1) {
                    $txt['error_new_reply'] = isset($_GET['last_msg']) ? $txt['error_new_reply_reading'] : $txt['error_new_reply'];
                } else {
                    $txt['error_new_replies'] = sprintf(isset($_GET['last_msg']) ? $txt['error_new_replies_reading'] : $txt['error_new_replies'], $context['new_replies']);
                }
                // If they've come from the display page then we treat the error differently....
                if (isset($_GET['last_msg'])) {
                    $newRepliesError = $context['new_replies'];
                } else {
                    $context['post_error'][$context['new_replies'] == 1 ? 'new_reply' : 'new_replies'] = true;
                }
                $modSettings['topicSummaryPosts'] = $context['new_replies'] > $modSettings['topicSummaryPosts'] ? max($modSettings['topicSummaryPosts'], 5) : $modSettings['topicSummaryPosts'];
            }
        }
        // Check whether this is a really old post being bumped...
        if (!empty($modSettings['oldTopicDays']) && $lastPostTime + $modSettings['oldTopicDays'] * 86400 < time() && empty($sticky) && !isset($_REQUEST['subject'])) {
            $oldTopicError = true;
        }
    }
    // Get a response prefix (like 'Re:') in the default forum language.
    if (!isset($context['response_prefix']) && !($context['response_prefix'] = cache_get_data('response_prefix'))) {
        if ($language === $user_info['language']) {
            $context['response_prefix'] = $txt['response_prefix'];
        } else {
            loadLanguage('index', $language, false);
            $context['response_prefix'] = $txt['response_prefix'];
            loadLanguage('index');
        }
        cache_put_data('response_prefix', $context['response_prefix'], 600);
    }
    // Previewing, modifying, or posting?
    if (isset($_REQUEST['message']) || !empty($context['post_error'])) {
        // Validate inputs.
        if (empty($context['post_error'])) {
            if (htmltrim__recursive(htmlspecialchars__recursive($_REQUEST['subject'])) == '') {
                $context['post_error']['no_subject'] = true;
            }
            if (htmltrim__recursive(htmlspecialchars__recursive($_REQUEST['message'])) == '') {
                $context['post_error']['no_message'] = true;
            }
            if (!empty($modSettings['max_messageLength']) && $smcFunc['strlen']($_REQUEST['message']) > $modSettings['max_messageLength']) {
                $context['post_error']['long_message'] = true;
            }
            // Are you... a guest?
            if ($user_info['is_guest']) {
                $_REQUEST['guestname'] = !isset($_REQUEST['guestname']) ? '' : trim($_REQUEST['guestname']);
                $_REQUEST['email'] = !isset($_REQUEST['email']) ? '' : trim($_REQUEST['email']);
                // Validate the name and email.
                if (!isset($_REQUEST['guestname']) || trim(strtr($_REQUEST['guestname'], '_', ' ')) == '') {
                    $context['post_error']['no_name'] = true;
                } elseif ($smcFunc['strlen']($_REQUEST['guestname']) > 25) {
                    $context['post_error']['long_name'] = true;
                } else {
                    require_once $sourcedir . '/Subs-Members.php';
                    if (isReservedName(htmlspecialchars($_REQUEST['guestname']), 0, true, false)) {
                        $context['post_error']['bad_name'] = true;
                    }
                }
                if (empty($modSettings['guest_post_no_email'])) {
                    if (!isset($_REQUEST['email']) || $_REQUEST['email'] == '') {
                        $context['post_error']['no_email'] = true;
                    } elseif (preg_match('~^[0-9A-Za-z=_+\\-/][0-9A-Za-z=_\'+\\-/\\.]*@[\\w\\-]+(\\.[\\w\\-]+)*(\\.[\\w]{2,6})$~', $_REQUEST['email']) == 0) {
                        $context['post_error']['bad_email'] = true;
                    }
                }
            }
            // This is self explanatory - got any questions?
            if (isset($_REQUEST['question']) && trim($_REQUEST['question']) == '') {
                $context['post_error']['no_question'] = true;
            }
            // This means they didn't click Post and get an error.
            $really_previewing = true;
        } else {
            if (!isset($_REQUEST['subject'])) {
                $_REQUEST['subject'] = '';
            }
            if (!isset($_REQUEST['message'])) {
                $_REQUEST['message'] = '';
            }
            if (!isset($_REQUEST['icon'])) {
                $_REQUEST['icon'] = 'xx';
            }
            // They are previewing if they asked to preview (i.e. came from quick reply).
            $really_previewing = !empty($_POST['preview']);
        }
        // In order to keep the approval status flowing through, we have to pass it through the form...
        $context['becomes_approved'] = empty($_REQUEST['not_approved']);
        $context['show_approval'] = isset($_REQUEST['approve']) ? $_REQUEST['approve'] ? 2 : 1 : 0;
        $context['can_announce'] &= $context['becomes_approved'];
        // Set up the inputs for the form.
        $form_subject = strtr($smcFunc['htmlspecialchars']($_REQUEST['subject']), array("\r" => '', "\n" => '', "\t" => ''));
        $form_message = $smcFunc['htmlspecialchars']($_REQUEST['message'], ENT_QUOTES);
        // Make sure the subject isn't too long - taking into account special characters.
        if ($smcFunc['strlen']($form_subject) > 100) {
            $form_subject = $smcFunc['substr']($form_subject, 0, 100);
        }
        // Have we inadvertently trimmed off the subject of useful information?
        if ($smcFunc['htmltrim']($form_subject) === '') {
            $context['post_error']['no_subject'] = true;
        }
        // Any errors occurred?
        if (!empty($context['post_error'])) {
            loadLanguage('Errors');
            $context['error_type'] = 'minor';
            $context['post_error']['messages'] = array();
            foreach ($context['post_error'] as $post_error => $dummy) {
                if ($post_error == 'messages') {
                    continue;
                }
                if ($post_error == 'long_message') {
                    $txt['error_' . $post_error] = sprintf($txt['error_' . $post_error], $modSettings['max_messageLength']);
                }
                $context['post_error']['messages'][] = $txt['error_' . $post_error];
                // If it's not a minor error flag it as such.
                if (!in_array($post_error, array('new_reply', 'not_approved', 'new_replies', 'old_topic', 'need_qr_verification'))) {
                    $context['error_type'] = 'serious';
                }
            }
        }
        if (isset($_REQUEST['poll'])) {
            $context['question'] = isset($_REQUEST['question']) ? $smcFunc['htmlspecialchars'](trim($_REQUEST['question'])) : '';
            $context['choices'] = array();
            $choice_id = 0;
            $_POST['options'] = empty($_POST['options']) ? array() : htmlspecialchars__recursive($_POST['options']);
            foreach ($_POST['options'] as $option) {
                if (trim($option) == '') {
                    continue;
                }
                $context['choices'][] = array('id' => $choice_id++, 'number' => $choice_id, 'label' => $option, 'is_last' => false);
            }
            if (count($context['choices']) < 2) {
                $context['choices'][] = array('id' => $choice_id++, 'number' => $choice_id, 'label' => '', 'is_last' => false);
                $context['choices'][] = array('id' => $choice_id++, 'number' => $choice_id, 'label' => '', 'is_last' => false);
            }
            $context['choices'][count($context['choices']) - 1]['is_last'] = true;
        }
        // Are you... a guest?
        if ($user_info['is_guest']) {
            $_REQUEST['guestname'] = !isset($_REQUEST['guestname']) ? '' : trim($_REQUEST['guestname']);
            $_REQUEST['email'] = !isset($_REQUEST['email']) ? '' : trim($_REQUEST['email']);
            $_REQUEST['guestname'] = htmlspecialchars($_REQUEST['guestname']);
            $context['name'] = $_REQUEST['guestname'];
            $_REQUEST['email'] = htmlspecialchars($_REQUEST['email']);
            $context['email'] = $_REQUEST['email'];
            $user_info['name'] = $_REQUEST['guestname'];
        }
        // Only show the preview stuff if they hit Preview.
        if ($really_previewing == true || isset($_REQUEST['xml'])) {
            // Set up the preview message and subject and censor them...
            $context['preview_message'] = $form_message;
            preparsecode($form_message, true);
            preparsecode($context['preview_message']);
            // Do all bulletin board code tags, with or without smileys.
            $context['preview_message'] = parse_bbc($context['preview_message'], isset($_REQUEST['ns']) ? 0 : 1);
            if ($form_subject != '') {
                $context['preview_subject'] = $form_subject;
                censorText($context['preview_subject']);
                censorText($context['preview_message']);
            } else {
                $context['preview_subject'] = '<em>' . $txt['no_subject'] . '</em>';
            }
            // Protect any CDATA blocks.
            if (isset($_REQUEST['xml'])) {
                $context['preview_message'] = strtr($context['preview_message'], array(']]>' => ']]]]><![CDATA[>'));
            }
        }
        // Set up the checkboxes.
        $context['notify'] = !empty($_REQUEST['notify']);
        $context['use_smileys'] = !isset($_REQUEST['ns']);
        $context['icon'] = isset($_REQUEST['icon']) ? preg_replace('~[\\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : 'xx';
        // Set the destination action for submission.
        $context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['msg']) ? ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] : '') . (isset($_REQUEST['poll']) ? ';poll' : '');
        $context['submit_label'] = isset($_REQUEST['msg']) ? $txt['save'] : $txt['post'];
        // Previewing an edit?
        if (isset($_REQUEST['msg']) && !empty($topic)) {
            // Get the existing message.
            $request = $smcFunc['db_query']('', '
				SELECT
					m.id_member, m.modified_time, m.smileys_enabled, m.body,
					m.poster_name, m.poster_email, m.subject, m.icon, m.approved,
					IFNULL(a.size, -1) AS filesize, a.filename, a.id_attach,
					a.approved AS attachment_approved, t.id_member_started AS id_member_poster,
					m.poster_time
			FROM {db_prefix}messages AS m
					INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
					LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = m.id_msg AND a.attachment_type = {int:attachment_type})
				WHERE m.id_msg = {int:id_msg}
					AND m.id_topic = {int:current_topic}', array('current_topic' => $topic, 'attachment_type' => 0, 'id_msg' => $_REQUEST['msg']));
            // The message they were trying to edit was most likely deleted.
            // !!! Change this error message?
            if ($smcFunc['db_num_rows']($request) == 0) {
                fatal_lang_error('no_board', false);
            }
            $row = $smcFunc['db_fetch_assoc']($request);
            $attachment_stuff = array($row);
            while ($row2 = $smcFunc['db_fetch_assoc']($request)) {
                $attachment_stuff[] = $row2;
            }
            $smcFunc['db_free_result']($request);
            if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any')) {
                // Give an extra five minutes over the disable time threshold, so they can type - assuming the post is public.
                if ($row['approved'] && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time()) {
                    fatal_lang_error('modify_post_time_passed', false);
                } elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_own')) {
                    isAllowedTo('modify_replies');
                } else {
                    isAllowedTo('modify_own');
                }
            } elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_any')) {
                isAllowedTo('modify_replies');
            } else {
                isAllowedTo('modify_any');
            }
            if (!empty($modSettings['attachmentEnable'])) {
                $request = $smcFunc['db_query']('', '
					SELECT IFNULL(size, -1) AS filesize, filename, id_attach, approved
					FROM {db_prefix}attachments
					WHERE id_msg = {int:id_msg}
						AND attachment_type = {int:attachment_type}', array('id_msg' => (int) $_REQUEST['msg'], 'attachment_type' => 0));
                while ($row = $smcFunc['db_fetch_assoc']($request)) {
                    if ($row['filesize'] <= 0) {
                        continue;
                    }
                    $context['current_attachments'][] = array('name' => htmlspecialchars($row['filename']), 'id' => $row['id_attach'], 'approved' => $row['approved']);
                }
                $smcFunc['db_free_result']($request);
            }
            // Allow moderators to change names....
            if (allowedTo('moderate_forum') && !empty($topic)) {
                $request = $smcFunc['db_query']('', '
					SELECT id_member, poster_name, poster_email
					FROM {db_prefix}messages
					WHERE id_msg = {int:id_msg}
						AND id_topic = {int:current_topic}
					LIMIT 1', array('current_topic' => $topic, 'id_msg' => (int) $_REQUEST['msg']));
                $row = $smcFunc['db_fetch_assoc']($request);
                $smcFunc['db_free_result']($request);
                if (empty($row['id_member'])) {
                    $context['name'] = htmlspecialchars($row['poster_name']);
                    $context['email'] = htmlspecialchars($row['poster_email']);
                }
            }
        }
        // No check is needed, since nothing is really posted.
        checkSubmitOnce('free');
    } elseif (isset($_REQUEST['msg']) && !empty($topic)) {
        $_REQUEST['msg'] = (int) $_REQUEST['msg'];
        // Get the existing message.
        $request = $smcFunc['db_query']('', '
			SELECT
				m.id_member, m.modified_time, m.smileys_enabled, m.body,
				m.poster_name, m.poster_email, m.subject, m.icon, m.approved,
				IFNULL(a.size, -1) AS filesize, a.filename, a.id_attach,
				a.approved AS attachment_approved, t.id_member_started AS id_member_poster,
				m.poster_time
			FROM {db_prefix}messages AS m
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})
				LEFT JOIN {db_prefix}attachments AS a ON (a.id_msg = m.id_msg AND a.attachment_type = {int:attachment_type})
			WHERE m.id_msg = {int:id_msg}
				AND m.id_topic = {int:current_topic}', array('current_topic' => $topic, 'attachment_type' => 0, 'id_msg' => $_REQUEST['msg']));
        // The message they were trying to edit was most likely deleted.
        // !!! Change this error message?
        if ($smcFunc['db_num_rows']($request) == 0) {
            fatal_lang_error('no_board', false);
        }
        $row = $smcFunc['db_fetch_assoc']($request);
        $attachment_stuff = array($row);
        while ($row2 = $smcFunc['db_fetch_assoc']($request)) {
            $attachment_stuff[] = $row2;
        }
        $smcFunc['db_free_result']($request);
        if ($row['id_member'] == $user_info['id'] && !allowedTo('modify_any')) {
            // Give an extra five minutes over the disable time threshold, so they can type - assuming the post is public.
            if ($row['approved'] && !empty($modSettings['edit_disable_time']) && $row['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time()) {
                fatal_lang_error('modify_post_time_passed', false);
            } elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_own')) {
                isAllowedTo('modify_replies');
            } else {
                isAllowedTo('modify_own');
            }
        } elseif ($row['id_member_poster'] == $user_info['id'] && !allowedTo('modify_any')) {
            isAllowedTo('modify_replies');
        } else {
            isAllowedTo('modify_any');
        }
        // When was it last modified?
        if (!empty($row['modified_time'])) {
            $context['last_modified'] = timeformat($row['modified_time']);
        }
        // Get the stuff ready for the form.
        $form_subject = $row['subject'];
        $form_message = un_preparsecode($row['body']);
        censorText($form_message);
        censorText($form_subject);
        // Check the boxes that should be checked.
        $context['use_smileys'] = !empty($row['smileys_enabled']);
        $context['icon'] = $row['icon'];
        // Show an "approve" box if the user can approve it, and the message isn't approved.
        if (!$row['approved'] && !$context['show_approval']) {
            $context['show_approval'] = allowedTo('approve_posts');
        }
        // Load up 'em attachments!
        foreach ($attachment_stuff as $attachment) {
            if ($attachment['filesize'] >= 0 && !empty($modSettings['attachmentEnable'])) {
                $context['current_attachments'][] = array('name' => htmlspecialchars($attachment['filename']), 'id' => $attachment['id_attach'], 'approved' => $attachment['attachment_approved']);
            }
        }
        // Allow moderators to change names....
        if (allowedTo('moderate_forum') && empty($row['id_member'])) {
            $context['name'] = htmlspecialchars($row['poster_name']);
            $context['email'] = htmlspecialchars($row['poster_email']);
        }
        // Set the destinaton.
        $context['destination'] = 'post2;start=' . $_REQUEST['start'] . ';msg=' . $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] . (isset($_REQUEST['poll']) ? ';poll' : '');
        $context['submit_label'] = $txt['save'];
    } else {
        // By default....
        $context['use_smileys'] = true;
        $context['icon'] = 'xx';
        if ($user_info['is_guest']) {
            $context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : '';
            $context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : '';
        }
        $context['destination'] = 'post2;start=' . $_REQUEST['start'] . (isset($_REQUEST['poll']) ? ';poll' : '');
        $context['submit_label'] = $txt['post'];
        // Posting a quoted reply?
        if (!empty($topic) && !empty($_REQUEST['quote'])) {
            $pids = array_filter(array_unique(array_map('intval', explode('-', $_REQUEST['quote']))));
            $message_content = '';
            foreach ($pids as $value) {
                // Make sure they _can_ quote this post, and if so get it.
                $request = $smcFunc['db_query']('', '
					SELECT m.subject, IFNULL(mem.real_name, m.poster_name) AS poster_name, m.poster_time, m.body
					FROM {db_prefix}messages AS m
						INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board AND {query_see_board})
						LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
					WHERE m.id_msg = {int:id_msg}' . (!$modSettings['postmod_active'] || allowedTo('approve_posts') ? '' : '
						AND m.approved = {int:is_approved}') . '
					LIMIT 1', array('id_msg' => (int) $value, 'is_approved' => 1));
                if ($smcFunc['db_num_rows']($request) == 0) {
                    fatal_lang_error('quoted_post_deleted', false);
                }
                list($form_subject, $mname, $mdate, $form_message) = $smcFunc['db_fetch_row']($request);
                $smcFunc['db_free_result']($request);
                // Add 'Re: ' to the front of the quoted subject.
                if (trim($context['response_prefix']) != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0) {
                    $form_subject = $context['response_prefix'] . $form_subject;
                }
                // Censor the message and subject.
                censorText($form_message);
                censorText($form_subject);
                // But if it's in HTML world, turn them into htmlspecialchar's so they can be edited!
                if (strpos($form_message, '[html]') !== false) {
                    $parts = preg_split('~(\\[/code\\]|\\[code(?:=[^\\]]+)?\\])~i', $form_message, -1, PREG_SPLIT_DELIM_CAPTURE);
                    for ($i = 0, $n = count($parts); $i < $n; $i++) {
                        // It goes 0 = outside, 1 = begin tag, 2 = inside, 3 = close tag, repeat.
                        if ($i % 4 == 0) {
                            $parts[$i] = preg_replace('~\\[html\\](.+?)\\[/html\\]~ise', '\'[html]\' . preg_replace(\'~<br\\s?/?' . '>~i\', \'&lt;br /&gt;<br />\', \'$1\') . \'[/html]\'', $parts[$i]);
                        }
                    }
                    $form_message = implode('', $parts);
                }
                $form_message = preg_replace('~<br ?/?' . '>~i', "\n", $form_message);
                // Remove any nested quotes, if necessary.
                if (!empty($modSettings['removeNestedQuotes'])) {
                    $form_message = preg_replace(array('~\\n?\\[quote.*?\\].+?\\[/quote\\]\\n?~is', '~^\\n~', '~\\[/quote\\]~'), '', $form_message);
                }
                // Add a quote string on the front and end.
                $message_content .= '[quote author=' . $mname . ' link=topic=' . $topic . '.msg' . (int) $value . '#msg' . (int) $value . ' date=' . $mdate . ']' . "\n" . rtrim($form_message) . "\n[/quote]\n\n";
            }
            $form_message = $message_content;
        } elseif (!empty($topic) && empty($_REQUEST['quote'])) {
            // Get the first message's subject.
            $form_subject = $first_subject;
            // Add 'Re: ' to the front of the subject.
            if (trim($context['response_prefix']) != '' && $form_subject != '' && $smcFunc['strpos']($form_subject, trim($context['response_prefix'])) !== 0) {
                $form_subject = $context['response_prefix'] . $form_subject;
            }
            // Censor the subject.
            censorText($form_subject);
            $form_message = '';
        } else {
            $form_subject = isset($_GET['subject']) ? $_GET['subject'] : '';
            $form_message = '';
        }
    }
    // !!! This won't work if you're posting an event.
    if (allowedTo('post_attachment') || allowedTo('post_unapproved_attachments')) {
        if (empty($_SESSION['temp_attachments'])) {
            $_SESSION['temp_attachments'] = array();
        }
        if (!empty($modSettings['currentAttachmentUploadDir'])) {
            if (!is_array($modSettings['attachmentUploadDir'])) {
                $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
            }
            // Just use the current path for temp files.
            $current_attach_dir = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
        } else {
            $current_attach_dir = $modSettings['attachmentUploadDir'];
        }
        // If this isn't a new post, check the current attachments.
        if (isset($_REQUEST['msg'])) {
            $request = $smcFunc['db_query']('', '
				SELECT COUNT(*), SUM(size)
				FROM {db_prefix}attachments
				WHERE id_msg = {int:id_msg}
					AND attachment_type = {int:attachment_type}', array('id_msg' => (int) $_REQUEST['msg'], 'attachment_type' => 0));
            list($quantity, $total_size) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
        } else {
            $quantity = 0;
            $total_size = 0;
        }
        $temp_start = 0;
        if (!empty($_SESSION['temp_attachments'])) {
            if ($context['current_action'] != 'post2' || !empty($_POST['from_qr'])) {
                $context['post_error']['messages'][] = $txt['error_temp_attachments'];
                $context['error_type'] = 'minor';
            }
            foreach ($_SESSION['temp_attachments'] as $attachID => $name) {
                $temp_start++;
                if (preg_match('~^post_tmp_' . $user_info['id'] . '_\\d+$~', $attachID) == 0) {
                    unset($_SESSION['temp_attachments'][$attachID]);
                    continue;
                }
                if (!empty($_POST['attach_del']) && !in_array($attachID, $_POST['attach_del'])) {
                    $deleted_attachments = true;
                    unset($_SESSION['temp_attachments'][$attachID]);
                    @unlink($current_attach_dir . '/' . $attachID);
                    continue;
                }
                $quantity++;
                $total_size += filesize($current_attach_dir . '/' . $attachID);
                $context['current_attachments'][] = array('name' => htmlspecialchars($name), 'id' => $attachID, 'approved' => 1);
            }
        }
        if (!empty($_POST['attach_del'])) {
            $del_temp = array();
            foreach ($_POST['attach_del'] as $i => $dummy) {
                $del_temp[$i] = (int) $dummy;
            }
            foreach ($context['current_attachments'] as $k => $dummy) {
                if (!in_array($dummy['id'], $del_temp)) {
                    $context['current_attachments'][$k]['unchecked'] = true;
                    $deleted_attachments = !isset($deleted_attachments) || is_bool($deleted_attachments) ? 1 : $deleted_attachments + 1;
                    $quantity--;
                }
            }
        }
        if (!empty($_FILES['attachment'])) {
            foreach ($_FILES['attachment']['tmp_name'] as $n => $dummy) {
                if ($_FILES['attachment']['name'][$n] == '') {
                    continue;
                }
                if (!is_uploaded_file($_FILES['attachment']['tmp_name'][$n]) || @ini_get('open_basedir') == '' && !file_exists($_FILES['attachment']['tmp_name'][$n])) {
                    fatal_lang_error('attach_timeout', 'critical');
                }
                if (!empty($modSettings['attachmentSizeLimit']) && $_FILES['attachment']['size'][$n] > $modSettings['attachmentSizeLimit'] * 1024) {
                    fatal_lang_error('file_too_big', false, array($modSettings['attachmentSizeLimit']));
                }
                $quantity++;
                if (!empty($modSettings['attachmentNumPerPostLimit']) && $quantity > $modSettings['attachmentNumPerPostLimit']) {
                    fatal_lang_error('attachments_limit_per_post', false, array($modSettings['attachmentNumPerPostLimit']));
                }
                $total_size += $_FILES['attachment']['size'][$n];
                if (!empty($modSettings['attachmentPostLimit']) && $total_size > $modSettings['attachmentPostLimit'] * 1024) {
                    fatal_lang_error('file_too_big', false, array($modSettings['attachmentPostLimit']));
                }
                if (!empty($modSettings['attachmentCheckExtensions'])) {
                    if (!in_array(strtolower(substr(strrchr($_FILES['attachment']['name'][$n], '.'), 1)), explode(',', strtolower($modSettings['attachmentExtensions'])))) {
                        fatal_error($_FILES['attachment']['name'][$n] . '.<br />' . $txt['cant_upload_type'] . ' ' . $modSettings['attachmentExtensions'] . '.', false);
                    }
                }
                if (!empty($modSettings['attachmentDirSizeLimit'])) {
                    // Make sure the directory isn't full.
                    $dirSize = 0;
                    $dir = @opendir($current_attach_dir) or fatal_lang_error('cant_access_upload_path', 'critical');
                    while ($file = readdir($dir)) {
                        if ($file == '.' || $file == '..') {
                            continue;
                        }
                        if (preg_match('~^post_tmp_\\d+_\\d+$~', $file) != 0) {
                            // Temp file is more than 5 hours old!
                            if (filemtime($current_attach_dir . '/' . $file) < time() - 18000) {
                                @unlink($current_attach_dir . '/' . $file);
                            }
                            continue;
                        }
                        $dirSize += filesize($current_attach_dir . '/' . $file);
                    }
                    closedir($dir);
                    // Too big!  Maybe you could zip it or something...
                    if ($_FILES['attachment']['size'][$n] + $dirSize > $modSettings['attachmentDirSizeLimit'] * 1024) {
                        fatal_lang_error('ran_out_of_space');
                    }
                }
                if (!is_writable($current_attach_dir)) {
                    fatal_lang_error('attachments_no_write', 'critical');
                }
                $attachID = 'post_tmp_' . $user_info['id'] . '_' . $temp_start++;
                $_SESSION['temp_attachments'][$attachID] = basename($_FILES['attachment']['name'][$n]);
                $context['current_attachments'][] = array('name' => htmlspecialchars(basename($_FILES['attachment']['name'][$n])), 'id' => $attachID, 'approved' => 1);
                $destName = $current_attach_dir . '/' . $attachID;
                if (!move_uploaded_file($_FILES['attachment']['tmp_name'][$n], $destName)) {
                    fatal_lang_error('attach_timeout', 'critical');
                }
                @chmod($destName, 0644);
            }
        }
    }
    // If we are coming here to make a reply, and someone has already replied... make a special warning message.
    if (isset($newRepliesError)) {
        $context['post_error']['messages'][] = $newRepliesError == 1 ? $txt['error_new_reply'] : $txt['error_new_replies'];
        $context['error_type'] = 'minor';
    }
    if (isset($oldTopicError)) {
        $context['post_error']['messages'][] = sprintf($txt['error_old_topic'], $modSettings['oldTopicDays']);
        $context['error_type'] = 'minor';
    }
    // What are you doing?  Posting a poll, modifying, previewing, new post, or reply...
    if (isset($_REQUEST['poll'])) {
        $context['page_title'] = $txt['new_poll'];
    } elseif ($context['make_event']) {
        $context['page_title'] = $context['event']['id'] == -1 ? $txt['calendar_post_event'] : $txt['calendar_edit'];
    } elseif (isset($_REQUEST['msg'])) {
        $context['page_title'] = $txt['modify_msg'];
    } elseif (isset($_REQUEST['subject'], $context['preview_subject'])) {
        $context['page_title'] = $txt['preview'] . ' - ' . strip_tags($context['preview_subject']);
    } elseif (empty($topic)) {
        $context['page_title'] = $txt['start_new_topic'];
    } else {
        $context['page_title'] = $txt['post_reply'];
    }
    // Build the link tree.
    if (empty($topic)) {
        $context['linktree'][] = array('name' => '<em>' . $txt['start_new_topic'] . '</em>');
    } else {
        $context['linktree'][] = array('url' => $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'], 'name' => $form_subject, 'extra_before' => '<span' . ($settings['linktree_inline'] ? ' class="smalltext"' : '') . '><strong class="nav">' . $context['page_title'] . ' ( </strong></span>', 'extra_after' => '<span' . ($settings['linktree_inline'] ? ' class="smalltext"' : '') . '><strong class="nav"> )</strong></span>');
    }
    // Give wireless a linktree url to the post screen, so that they can switch to full version.
    if (WIRELESS) {
        $context['linktree'][count($context['linktree']) - 1]['url'] = $scripturl . '?action=post;' . (!empty($topic) ? 'topic=' . $topic : 'board=' . $board) . '.' . $_REQUEST['start'] . (isset($_REQUEST['msg']) ? ';msg=' . (int) $_REQUEST['msg'] . ';' . $context['session_var'] . '=' . $context['session_id'] : '');
    }
    // If they've unchecked an attachment, they may still want to attach that many more files, but don't allow more than num_allowed_attachments.
    // !!! This won't work if you're posting an event.
    $context['num_allowed_attachments'] = empty($modSettings['attachmentNumPerPostLimit']) ? 50 : min($modSettings['attachmentNumPerPostLimit'] - count($context['current_attachments']) + (isset($deleted_attachments) ? $deleted_attachments : 0), $modSettings['attachmentNumPerPostLimit']);
    $context['can_post_attachment'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || $modSettings['postmod_active'] && allowedTo('post_unapproved_attachments')) && $context['num_allowed_attachments'] > 0;
    $context['can_post_attachment_unapproved'] = allowedTo('post_attachment');
    $context['subject'] = addcslashes($form_subject, '"');
    $context['message'] = str_replace(array('"', '<', '>', '&nbsp;'), array('&quot;', '&lt;', '&gt;', ' '), $form_message);
    // Needed for the editor and message icons.
    require_once $sourcedir . '/Subs-Editor.php';
    // Now create the editor.
    $editorOptions = array('id' => 'message', 'value' => $context['message'], 'labels' => array('post_button' => $context['submit_label']), 'height' => '175px', 'width' => '100%', 'preview_type' => 2);
    create_control_richedit($editorOptions);
    // Store the ID.
    $context['post_box_name'] = $editorOptions['id'];
    $context['attached'] = '';
    $context['make_poll'] = isset($_REQUEST['poll']);
    // Message icons - customized icons are off?
    $context['icons'] = getMessageIcons($board);
    if (!empty($context['icons'])) {
        $context['icons'][count($context['icons']) - 1]['is_last'] = true;
    }
    $context['icon_url'] = '';
    for ($i = 0, $n = count($context['icons']); $i < $n; $i++) {
        $context['icons'][$i]['selected'] = $context['icon'] == $context['icons'][$i]['value'];
        if ($context['icons'][$i]['selected']) {
            $context['icon_url'] = $context['icons'][$i]['url'];
        }
    }
    if (empty($context['icon_url'])) {
        $context['icon_url'] = $settings[file_exists($settings['theme_dir'] . '/images/post/' . $context['icon'] . '.gif') ? 'images_url' : 'default_images_url'] . '/post/' . $context['icon'] . '.gif';
        array_unshift($context['icons'], array('value' => $context['icon'], 'name' => $txt['current_icon'], 'url' => $context['icon_url'], 'is_last' => empty($context['icons']), 'selected' => true));
    }
    if (!empty($topic) && !empty($modSettings['topicSummaryPosts'])) {
        getTopic();
    }
    // If the user can post attachments prepare the warning labels.
    if ($context['can_post_attachment']) {
        $context['allowed_extensions'] = strtr($modSettings['attachmentExtensions'], array(',' => ', '));
        $context['attachment_restrictions'] = array();
        $attachmentRestrictionTypes = array('attachmentNumPerPostLimit', 'attachmentPostLimit', 'attachmentSizeLimit');
        foreach ($attachmentRestrictionTypes as $type) {
            if (!empty($modSettings[$type])) {
                $context['attachment_restrictions'][] = sprintf($txt['attach_restrict_' . $type], $modSettings[$type]);
            }
        }
    }
    $context['back_to_topic'] = isset($_REQUEST['goback']) || isset($_REQUEST['msg']) && !isset($_REQUEST['subject']);
    $context['show_additional_options'] = !empty($_POST['additional_options']) || !empty($_SESSION['temp_attachments']) || !empty($deleted_attachments);
    $context['is_new_topic'] = empty($topic);
    $context['is_new_post'] = !isset($_REQUEST['msg']);
    $context['is_first_post'] = $context['is_new_topic'] || isset($_REQUEST['msg']) && $_REQUEST['msg'] == $id_first_msg;
    // Do we need to show the visual verification image?
    $context['require_verification'] = !$user_info['is_mod'] && !$user_info['is_admin'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || $user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1);
    if ($context['require_verification']) {
        require_once $sourcedir . '/Subs-Editor.php';
        $verificationOptions = array('id' => 'post');
        $context['require_verification'] = create_control_verification($verificationOptions);
        $context['visual_verification_id'] = $verificationOptions['id'];
    }
    // If they came from quick reply, and have to enter verification details, give them some notice.
    if (!empty($_REQUEST['from_qr']) && !empty($context['require_verification'])) {
        $context['post_error']['messages'][] = $txt['enter_verification_details'];
        $context['error_type'] = 'minor';
    }
    // WYSIWYG only works if BBC is enabled
    $modSettings['disable_wysiwyg'] = !empty($modSettings['disable_wysiwyg']) || empty($modSettings['enableBBC']);
    // Register this form in the session variables.
    checkSubmitOnce('register');
    // Finally, load the template.
    if (WIRELESS && WIRELESS_PROTOCOL != 'wap') {
        $context['sub_template'] = WIRELESS_PROTOCOL . '_post';
    } elseif (!isset($_REQUEST['xml'])) {
        loadTemplate('Post');
    }
}