public static function boardindex() { global $context, $txt, $sourcedir, $user_info; $data = array(); if (($data = CacheAPI::getCache('github-feed-index', 1800)) === null) { $f = curl_init(); if ($f) { curl_setopt_array($f, array(CURLOPT_URL => self::$my_git_api_url, CURLOPT_HEADER => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_SSL_VERIFYPEER => false)); $json_response = curl_exec($f); $data = json_decode($json_response, true); CacheAPI::putCache('github-feed-index', $data, 1800); curl_close($f); } } $n = 0; foreach ($data as $commit) { if (is_array($commit) && isset($commit['commit'])) { $context['gitfeed'][] = array('message_short' => shorten_subject($commit['commit']['message'], 60), 'message' => nl2br($commit['commit']['message']), 'dateline' => timeformat(strtotime($commit['commit']['committer']['date'])), 'sha' => $commit['sha'], 'href' => self::$my_git_url . 'commit/' . $commit['sha']); } if (++$n > 5) { break; } } if (!empty($data)) { /* * add our plugin directory to the list of directories to search for templates * and register the template hook. * only do this if we actually have something to display */ EoS_Smarty::addTemplateDir(dirname(__FILE__)); EoS_Smarty::getConfigInstance()->registerHookTemplate('sidebar_below_userblock', 'gitfeed_sidebar_top'); $context['gitfeed_global']['see_all']['href'] = self::$my_git_url . 'commits/master'; $context['gitfeed_global']['see_all']['txt'] = 'Browse all commits'; } }
function ShowXmlFeed() { global $board, $board_info, $context, $scripturl, $txt, $modSettings, $user_info; global $query_this_board, $smcFunc, $forum_version, $cdata_override; // If it's not enabled, die. if (empty($modSettings['xmlnews_enable'])) { obExit(false); } loadLanguage('Stats'); // Default to latest 5. No more than 255, please. $_GET['limit'] = empty($_GET['limit']) || (int) $_GET['limit'] < 1 ? 5 : min((int) $_GET['limit'], 255); // Handle the cases where a board, boards, or category is asked for. $query_this_board = 1; $context['optimize_msg'] = array('highest' => 'm.id_msg <= b.id_last_msg'); if (!empty($_REQUEST['c']) && empty($board)) { $_REQUEST['c'] = explode(',', $_REQUEST['c']); foreach ($_REQUEST['c'] as $i => $c) { $_REQUEST['c'][$i] = (int) $c; } if (count($_REQUEST['c']) == 1) { $request = smf_db_query(' SELECT name FROM {db_prefix}categories WHERE id_cat = {int:current_category}', array('current_category' => (int) $_REQUEST['c'][0])); list($feed_title) = mysql_fetch_row($request); mysql_free_result($request); $feed_title = ' - ' . strip_tags($feed_title); } $request = smf_db_query(' SELECT b.id_board, b.num_posts FROM {db_prefix}boards AS b WHERE b.id_cat IN ({array_int:current_category_list}) AND {query_see_board}', array('current_category_list' => $_REQUEST['c'])); $total_cat_posts = 0; $boards = array(); while ($row = mysql_fetch_assoc($request)) { $boards[] = $row['id_board']; $total_cat_posts += $row['num_posts']; } mysql_free_result($request); if (!empty($boards)) { $query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')'; } // Try to limit the number of messages we look through. if ($total_cat_posts > 100 && $total_cat_posts > $modSettings['totalMessages'] / 15) { $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 400 - $_GET['limit'] * 5); } } elseif (!empty($_REQUEST['boards'])) { $_REQUEST['boards'] = explode(',', $_REQUEST['boards']); foreach ($_REQUEST['boards'] as $i => $b) { $_REQUEST['boards'][$i] = (int) $b; } $request = smf_db_query(' SELECT b.id_board, b.num_posts, b.name FROM {db_prefix}boards AS b WHERE b.id_board IN ({array_int:board_list}) AND {query_see_board} LIMIT ' . count($_REQUEST['boards']), array('board_list' => $_REQUEST['boards'])); // Either the board specified doesn't exist or you have no access. $num_boards = mysql_num_rows($request); if ($num_boards == 0) { fatal_lang_error('no_board'); } $total_posts = 0; $boards = array(); while ($row = mysql_fetch_assoc($request)) { if ($num_boards == 1) { $feed_title = ' - ' . strip_tags($row['name']); } $boards[] = $row['id_board']; $total_posts += $row['num_posts']; } mysql_free_result($request); if (!empty($boards)) { $query_this_board = 'b.id_board IN (' . implode(', ', $boards) . ')'; } // The more boards, the more we're going to look through... if ($total_posts > 100 && $total_posts > $modSettings['totalMessages'] / 12) { $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 500 - $_GET['limit'] * 5); } } elseif (!empty($board)) { $request = smf_db_query(' SELECT num_posts FROM {db_prefix}boards WHERE id_board = {int:current_board} LIMIT 1', array('current_board' => $board)); list($total_posts) = mysql_fetch_row($request); mysql_free_result($request); $feed_title = ' - ' . strip_tags($board_info['name']); $query_this_board = 'b.id_board = ' . $board; // Try to look through just a few messages, if at all possible. if ($total_posts > 80 && $total_posts > $modSettings['totalMessages'] / 10) { $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 600 - $_GET['limit'] * 5); } } else { $query_this_board = '{query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' AND b.id_board != ' . $modSettings['recycle_board'] : ''); $context['optimize_msg']['lowest'] = 'm.id_msg >= ' . max(0, $modSettings['maxMsgID'] - 100 - $_GET['limit'] * 5); } // Show in rss or proprietary format? $xml_format = isset($_GET['type']) && in_array($_GET['type'], array('smf', 'rss', 'rss2', 'atom', 'rdf', 'webslice')) ? $_GET['type'] : 'smf'; // !!! Birthdays? // List all the different types of data they can pull. $subActions = array('recent' => array('getXmlRecent', 'recent-post'), 'news' => array('getXmlNews', 'article'), 'members' => array('getXmlMembers', 'member'), 'profile' => array('getXmlProfile', null)); if (empty($_GET['sa']) || !isset($subActions[$_GET['sa']])) { $_GET['sa'] = 'recent'; } //!!! Temp - webslices doesn't do everything yet. if ($xml_format == 'webslice' && $_GET['sa'] != 'recent') { $xml_format = 'rss2'; } elseif ($xml_format == 'webslice') { $context['user'] += $user_info; $cdata_override = true; loadTemplate('Xml'); } // We only want some information, not all of it. $cachekey = array($xml_format, $_GET['action'], $_GET['limit'], $_GET['sa']); foreach (array('board', 'boards', 'c') as $var) { if (isset($_REQUEST[$var])) { $cachekey[] = $_REQUEST[$var]; } } $cachekey = md5(serialize($cachekey) . (!empty($query_this_board) ? $query_this_board : '')); $cache_t = microtime(); // Get the associative array representing the xml. if (!empty($modSettings['cache_enable']) && (!$user_info['is_guest'] || $modSettings['cache_enable'] >= 3)) { $xml = CacheAPI::getCache('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, 240); } if (empty($xml)) { $xml = $subActions[$_GET['sa']][0]($xml_format); if (!empty($modSettings['cache_enable']) && ($user_info['is_guest'] && $modSettings['cache_enable'] >= 3 || !$user_info['is_guest'] && array_sum(explode(' ', microtime())) - array_sum(explode(' ', $cache_t)) > 0.2)) { CacheAPI::putCache('xmlfeed-' . $xml_format . ':' . ($user_info['is_guest'] ? '' : $user_info['id'] . '-') . $cachekey, $xml, 240); } } $feed_title = htmlspecialchars(strip_tags($context['forum_name'])) . (isset($feed_title) ? $feed_title : ''); // This is an xml file.... ob_end_clean(); if (!empty($modSettings['enableCompressedOutput'])) { @ob_start('ob_gzhandler'); } else { ob_start(); } if ($xml_format == 'smf' || isset($_REQUEST['debug'])) { header('Content-Type: text/xml; charset=UTF-8'); } elseif ($xml_format == 'rss' || $xml_format == 'rss2' || $xml_format == 'webslice') { header('Content-Type: application/rss+xml; charset=UTF-8'); } elseif ($xml_format == 'atom') { header('Content-Type: application/atom+xml; charset=UTF-8'); } elseif ($xml_format == 'rdf') { header('Content-Type: ' . ($context['browser']['is_ie'] ? 'text/xml' : 'application/rdf+xml') . '; charset=UTF-8'); } // First, output the xml header. echo '<?xml version="1.0" encoding="UTF-8"?' . '>'; // Are we outputting an rss feed or one with more information? if ($xml_format == 'rss' || $xml_format == 'rss2') { // Start with an RSS 2.0 header. echo ' <rss version=', $xml_format == 'rss2' ? '"2.0"' : '"0.92"', ' xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"> <channel> <title>', $feed_title, '</title> <link>', $scripturl, '</link> <description><![CDATA[', strip_tags($txt['xml_rss_desc']), ']]></description>'; // Output all of the associative array, start indenting with 2 tabs, and name everything "item". dumpTags($xml, 2, 'item', $xml_format); // Output the footer of the xml. echo ' </channel> </rss>'; } elseif ($xml_format == 'webslice') { $context['recent_posts_data'] = $xml; // This always has RSS 2 echo ' <rss version="2.0" xmlns:mon="http://www.microsoft.com/schemas/rss/monitoring/2007" xml:lang="', strtr($txt['lang_locale'], '_', '-'), '"> <channel> <title>', $feed_title, ' - ', $txt['recent_posts'], '</title> <link>', $scripturl, '?action=recent</link> <description><![CDATA[', strip_tags($txt['xml_rss_desc']), ']]></description> <item> <title>', $feed_title, ' - ', $txt['recent_posts'], '</title> <link>', $scripturl, '?action=recent</link> <description><![CDATA[ ', template_webslice_header_above(), ' ', template_webslice_recent_posts(), ' ', template_webslice_header_below(), ' ]]></description> </item> </channel> </rss>'; } elseif ($xml_format == 'atom') { echo ' <feed xmlns="http://www.w3.org/2005/Atom"> <title>', $feed_title, '</title> <link rel="alternate" type="text/html" href="', $scripturl, '" /> <modified>', gmstrftime('%Y-%m-%dT%H:%M:%SZ'), '</modified> <tagline><![CDATA[', strip_tags($txt['xml_rss_desc']), ']]></tagline> <generator uri="http://www.simplemachines.org" version="', strtr($forum_version, array('SMF' => '')), '">SMF</generator> <author> <name>', strip_tags($context['forum_name']), '</name> </author>'; dumpTags($xml, 2, 'entry', $xml_format); echo ' </feed>'; } elseif ($xml_format == 'rdf') { echo ' <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://purl.org/rss/1.0/"> <channel rdf:about="', $scripturl, '"> <title>', $feed_title, '</title> <link>', $scripturl, '</link> <description><![CDATA[', strip_tags($txt['xml_rss_desc']), ']]></description> <items> <rdf:Seq>'; foreach ($xml as $item) { echo ' <rdf:li rdf:resource="', $item['link'], '" />'; } echo ' </rdf:Seq> </items> </channel> '; dumpTags($xml, 1, 'item', $xml_format); echo ' </rdf:RDF>'; } else { echo ' <smf:xml-feed xmlns:smf="http://www.simplemachines.org/" xmlns="http://www.simplemachines.org/xml/', $_GET['sa'], '" xml:lang="', strtr($txt['lang_locale'], '_', '-'), '">'; // Dump out that associative array. Indent properly.... and use the right names for the base elements. dumpTags($xml, 1, $subActions[$_GET['sa']][1], $xml_format); echo ' </smf:xml-feed>'; } obExit(false); }
function JavaScriptModify() { global $sourcedir, $modSettings, $board, $topic, $txt; global $user_info, $context, $language; // We have to have a topic! if (empty($topic)) { obExit(false); } checkSession('get'); require_once $sourcedir . '/lib/Subs-Post.php'; // Assume the first message if no message ID was given. $request = smf_db_query(' SELECT t.locked, t.num_replies, t.id_member_started, t.id_first_msg, m.id_msg, m.id_member, m.poster_time, m.subject, m.smileys_enabled, m.body, m.icon, m.modified_time, m.modified_name, m.approved, ba.id_topic AS banned_from_topic FROM {db_prefix}messages AS m INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic}) LEFT JOIN {db_prefix}topicbans AS ba ON (ba.id_topic = {int:current_topic} AND ba.id_member = {int:current_member}) WHERE m.id_msg = {raw:id_msg} AND m.id_topic = {int:current_topic}' . (allowedTo('approve_posts') ? '' : (!$modSettings['postmod_active'] ? ' AND (m.id_member != {int:guest_id} AND m.id_member = {int:current_member})' : ' AND (m.approved = {int:is_approved} OR (m.id_member != {int:guest_id} AND m.id_member = {int:current_member}))')), array('current_member' => $user_info['id'], 'current_topic' => $topic, 'id_msg' => empty($_REQUEST['msg']) ? 't.id_first_msg' : (int) $_REQUEST['msg'], 'is_approved' => 1, 'guest_id' => 0)); if (mysql_num_rows($request) == 0) { fatal_lang_error('no_board', false); } $row = mysql_fetch_assoc($request); mysql_free_result($request); // Change either body or subject requires permissions to modify messages. if (isset($_POST['message']) || isset($_POST['subject']) || isset($_REQUEST['icon'])) { if (!empty($row['locked'])) { isAllowedTo('moderate_board'); } 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 ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_own')) { isAllowedTo('modify_replies'); } else { isAllowedTo('modify_own'); } } elseif ($row['id_member_started'] == $user_info['id'] && !allowedTo('modify_any')) { isAllowedTo('modify_replies'); } else { isAllowedTo('modify_any'); } // check topic bans if ($row['banned_from_topic'] != 0 && !$user_info['is_admin'] && !allowedTo('moderate_board') && !allowedTo('moderate_forum')) { fatal_lang_error('banned_from_topic'); } // Only log this action if it wasn't your message. $moderationAction = $row['id_member'] != $user_info['id']; } $post_errors = array(); if (isset($_POST['subject']) && commonAPI::htmltrim(commonAPI::htmlspecialchars($_POST['subject'])) !== '') { $_POST['subject'] = strtr(commonAPI::htmlspecialchars($_POST['subject']), array("\r" => '', "\n" => '', "\t" => '')); // Maximum number of characters. if (commonAPI::strlen($_POST['subject']) > 100) { $_POST['subject'] = commonAPI::substr($_POST['subject'], 0, 100); } } elseif (isset($_POST['subject'])) { $post_errors[] = 'no_subject'; unset($_POST['subject']); } if (isset($_POST['message'])) { if (commonAPI::htmltrim(commonAPI::htmlspecialchars($_POST['message'])) === '') { $post_errors[] = 'no_message'; unset($_POST['message']); } elseif (!empty($modSettings['max_messageLength']) && commonAPI::strlen($_POST['message']) > $modSettings['max_messageLength']) { $post_errors[] = 'long_message'; unset($_POST['message']); } else { $_POST['message'] = commonAPI::htmlspecialchars($_POST['message'], ENT_QUOTES); preparsecode($_POST['message']); if (commonAPI::htmltrim(strip_tags(parse_bbc($_POST['message'], false), '<img>')) === '') { $post_errors[] = 'no_message'; unset($_POST['message']); } } } if (isset($_POST['lock'])) { if (!allowedTo(array('lock_any', 'lock_own')) || !allowedTo('lock_any') && $user_info['id'] != $row['id_member']) { unset($_POST['lock']); } elseif (!allowedTo('lock_any')) { if ($row['locked'] == 1) { unset($_POST['lock']); } else { $_POST['lock'] = empty($_POST['lock']) ? 0 : 2; } } elseif (!empty($row['locked']) && !empty($_POST['lock']) || $_POST['lock'] == $row['locked']) { unset($_POST['lock']); } else { $_POST['lock'] = empty($_POST['lock']) ? 0 : 1; } } if (isset($_POST['sticky']) && !allowedTo('make_sticky')) { unset($_POST['sticky']); } if (empty($post_errors)) { $msgOptions = array('id' => $row['id_msg'], 'subject' => isset($_POST['subject']) ? $_POST['subject'] : null, 'body' => isset($_POST['message']) ? $_POST['message'] : null, 'icon' => isset($_REQUEST['icon']) ? preg_replace('~[\\./\\\\*\':"<>]~', '', $_REQUEST['icon']) : null, 'id_owner' => $row['id_member']); $topicOptions = array('id' => $topic, 'board' => $board, '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); $posterOptions = array(); // Only consider marking as editing if they have edited the subject, message or icon. if (isset($_POST['subject']) && $_POST['subject'] != $row['subject'] || isset($_POST['message']) && $_POST['message'] != $row['body'] || isset($_REQUEST['icon']) && $_REQUEST['icon'] != $row['icon']) { // And even then only if the time has passed... 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']; } } else { $moderationAction = false; } modifyPost($msgOptions, $topicOptions, $posterOptions); // If we didn't change anything this time but had before put back the old info. if (!isset($msgOptions['modify_time']) && !empty($row['modified_time'])) { $msgOptions['modify_time'] = $row['modified_time']; $msgOptions['modify_name'] = $row['modified_name']; } // Changing the first subject updates other subjects to 'Re: new_subject'. if (isset($_POST['subject']) && isset($_REQUEST['change_all_subjects']) && $row['id_first_msg'] == $row['id_msg'] && !empty($row['num_replies']) && (allowedTo('modify_any') || $row['id_member_started'] == $user_info['id'] && allowedTo('modify_replies'))) { // Get the proper (default language) response prefix first. if (!isset($context['response_prefix']) && !($context['response_prefix'] = CacheAPI::getCache('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'); } CacheAPI::putCache('response_prefix', $context['response_prefix'], 600); } smf_db_query(' UPDATE {db_prefix}messages SET subject = {string:subject} WHERE id_topic = {int:current_topic} AND id_msg != {int:id_first_msg}', array('current_topic' => $topic, 'id_first_msg' => $row['id_first_msg'], 'subject' => $context['response_prefix'] . $_POST['subject'])); } if (!empty($moderationAction)) { logAction('modify', array('topic' => $topic, 'message' => $row['id_msg'], 'member' => $row['id_member'], 'board' => $board)); } } if (isset($_REQUEST['xml'])) { $context['sub_template'] = 'modifydone'; if (empty($post_errors) && isset($msgOptions['subject']) && isset($msgOptions['body'])) { $context['message'] = array('id' => $row['id_msg'], 'modified' => array('time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '', 'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0, 'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : ''), 'subject' => $msgOptions['subject'], 'first_in_topic' => $row['id_msg'] == $row['id_first_msg'], 'body' => strtr($msgOptions['body'], array(']]>' => ']]]]><![CDATA[>'))); censorText($context['message']['subject']); censorText($context['message']['body']); $cache_key = isset($msgOptions['modify_time']) ? $row['id_msg'] . '|' . $msgOptions['modify_time'] : $row['id_msg']; $context['message']['body'] = parse_bbc($context['message']['body'], $row['smileys_enabled'], $cache_key); parse_bbc_stage2($context['message']['body']); } elseif (empty($post_errors)) { $context['sub_template'] = 'modifytopicdone'; $context['message'] = array('id' => $row['id_msg'], 'icon' => isset($_REQUEST['icon']) ? $_REQUEST['icon'] : '', 'modified' => array('time' => isset($msgOptions['modify_time']) ? timeformat($msgOptions['modify_time']) : '', 'timestamp' => isset($msgOptions['modify_time']) ? forum_time(true, $msgOptions['modify_time']) : 0, 'name' => isset($msgOptions['modify_time']) ? $msgOptions['modify_name'] : ''), 'subject' => isset($msgOptions['subject']) ? $msgOptions['subject'] : ''); censorText($context['message']['subject']); } else { $context['message'] = array('id' => $row['id_msg'], 'errors' => array(), 'error_in_subject' => in_array('no_subject', $post_errors), 'error_in_body' => in_array('no_message', $post_errors) || in_array('long_message', $post_errors)); loadLanguage('Errors'); foreach ($post_errors as $post_error) { if ($post_error == 'long_message') { $context['message']['errors'][] = sprintf($txt['error_' . $post_error], $modSettings['max_messageLength']); } else { $context['message']['errors'][] = $txt['error_' . $post_error]; } } } } else { obExit(false); } }
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']) . ' ' : ''; $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'] .= ' <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('&' => '&'))), '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); }
function MoveTopic2() { global $txt, $board, $topic, $scripturl, $sourcedir, $modSettings, $context; global $board, $language, $user_info, $smcFunc; if (empty($topic)) { fatal_lang_error('no_access', false); } // You can't choose to have a redirection topic and use an empty reason. if (isset($_POST['postRedirect']) && (!isset($_POST['reason']) || trim($_POST['reason']) == '')) { fatal_lang_error('movetopic_no_reason', false); } // Make sure this form hasn't been submitted before. checkSubmitOnce('check'); $request = smf_db_query(' SELECT id_member_started, id_first_msg, approved FROM {db_prefix}topics WHERE id_topic = {int:current_topic} LIMIT 1', array('current_topic' => $topic)); list($id_member_started, $id_first_msg, $context['is_approved']) = mysql_fetch_row($request); mysql_free_result($request); // Can they see it? if (!$context['is_approved']) { isAllowedTo('approve_posts'); } // Can they move topics on this board? if (!allowedTo('move_any')) { if ($id_member_started == $user_info['id']) { isAllowedTo('move_own'); $boards = array_merge(boardsAllowedTo('move_own'), boardsAllowedTo('move_any')); } else { isAllowedTo('move_any'); } } else { $boards = boardsAllowedTo('move_any'); } // If this topic isn't approved don't let them move it if they can't approve it! if ($modSettings['postmod_active'] && !$context['is_approved'] && !allowedTo('approve_posts')) { // Only allow them to move it to other boards they can't approve it in. $can_approve = boardsAllowedTo('approve_posts'); $boards = array_intersect($boards, $can_approve); } checkSession(); require_once $sourcedir . '/lib/Subs-Post.php'; // The destination board must be numeric. $_POST['toboard'] = (int) $_POST['toboard']; // Make sure they can see the board they are trying to move to (and get whether posts count in the target board). $request = smf_db_query(' SELECT b.count_posts, b.name, m.subject FROM {db_prefix}boards AS b INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic}) INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) WHERE {query_see_board} AND b.id_board = {int:to_board} AND b.redirect = {string:blank_redirect} LIMIT 1', array('current_topic' => $topic, 'to_board' => $_POST['toboard'], 'blank_redirect' => '')); if (mysql_num_rows($request) == 0) { fatal_lang_error('no_board'); } list($pcounter, $board_name, $subject) = mysql_fetch_row($request); mysql_free_result($request); // Remember this for later. $_SESSION['move_to_topic'] = $_POST['toboard']; // Rename the topic... if (isset($_POST['reset_subject'], $_POST['custom_subject']) && $_POST['custom_subject'] != '') { $_POST['custom_subject'] = strtr(commonAPI::htmltrim(commonAPI::htmlspecialchars($_POST['custom_subject'])), array("\r" => '', "\n" => '', "\t" => '')); // Keep checking the length. if (commonAPI::strlen($_POST['custom_subject']) > 100) { $_POST['custom_subject'] = commonAPI::substr($_POST['custom_subject'], 0, 100); } // If it's still valid move onwards and upwards. if ($_POST['custom_subject'] != '') { if (isset($_POST['enforce_subject'])) { // Get a response prefix, but in the forum's default language. if (!isset($context['response_prefix']) && !($context['response_prefix'] = CacheAPI::getCache('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'); } CacheAPI::putCache('response_prefix', $context['response_prefix'], 600); } smf_db_query(' UPDATE {db_prefix}messages SET subject = {string:subject} WHERE id_topic = {int:current_topic}', array('current_topic' => $topic, 'subject' => $context['response_prefix'] . $_POST['custom_subject'])); } smf_db_query(' UPDATE {db_prefix}messages SET subject = {string:custom_subject} WHERE id_msg = {int:id_first_msg}', array('id_first_msg' => $id_first_msg, 'custom_subject' => $_POST['custom_subject'])); // Fix the subject cache. updateStats('subject', $topic, $_POST['custom_subject']); } } // Create a link to this in the old board. //!!! Does this make sense if the topic was unapproved before? I'd just about say so. if (isset($_POST['postRedirect'])) { // Should be in the boardwide language. if ($user_info['language'] != $language) { loadLanguage('index', $language); } $_POST['reason'] = commonAPI::htmlspecialchars($_POST['reason'], ENT_QUOTES); preparsecode($_POST['reason']); // Add a URL onto the message. $_POST['reason'] = strtr($_POST['reason'], array($txt['movetopic_auto_board'] => '[url=' . $scripturl . '?board=' . $_POST['toboard'] . '.0]' . $board_name . '[/url]', $txt['movetopic_auto_topic'] => '[iurl]' . $scripturl . '?topic=' . $topic . '.0[/iurl]')); $msgOptions = array('subject' => $txt['moved'] . ': ' . $subject, 'body' => $_POST['reason'], 'icon' => 'moved', 'smileys_enabled' => 1); $topicOptions = array('board' => $board, 'lock_mode' => 1, 'mark_as_read' => true, 'topic_prefix' => 0, 'topic_layout' => 0); $posterOptions = array('id' => $user_info['id'], 'update_post_count' => empty($pcounter)); createPost($msgOptions, $topicOptions, $posterOptions); } $request = smf_db_query(' SELECT count_posts FROM {db_prefix}boards WHERE id_board = {int:current_board} LIMIT 1', array('current_board' => $board)); list($pcounter_from) = mysql_fetch_row($request); mysql_free_result($request); if ($pcounter_from != $pcounter) { $request = smf_db_query(' SELECT id_member FROM {db_prefix}messages WHERE id_topic = {int:current_topic} AND approved = {int:is_approved}', array('current_topic' => $topic, 'is_approved' => 1)); $posters = array(); while ($row = mysql_fetch_assoc($request)) { if (!isset($posters[$row['id_member']])) { $posters[$row['id_member']] = 0; } $posters[$row['id_member']]++; } mysql_free_result($request); foreach ($posters as $id_member => $posts) { // The board we're moving from counted posts, but not to. if (empty($pcounter_from)) { updateMemberData($id_member, array('posts' => 'posts - ' . $posts)); } else { updateMemberData($id_member, array('posts' => 'posts + ' . $posts)); } } } // Do the move (includes statistics update needed for the redirect topic). moveTopics($topic, $_POST['toboard']); // Log that they moved this topic. if (!allowedTo('move_own') || $id_member_started != $user_info['id']) { logAction('move', array('topic' => $topic, 'board_from' => $board, 'board_to' => $_POST['toboard'])); } // Notify people that this topic has been moved? sendNotifications($topic, 'move'); // Why not go back to the original board in case they want to keep moving? if (!isset($_REQUEST['goback'])) { redirectexit('board=' . $board . '.0'); } else { redirectexit('topic=' . $topic . '.0'); } }
/** * @param int $board board id or 0 * @param int $topic topic id or 0 * @param int $force_full if set, don't use the shortened versions with a "read more" link. * * fetch news for the board index or a specific board or topic. * Look at the current user group(s) to determine whether the user * is supposed to see the item. */ function fetchNewsItems($board = 0, $topic = 0, $force_full = false) { global $context, $user_info; $context['raw_news_items'] = array(); $context['news_items'] = array(); $context['news_item_count'] = 0; $context['can_dismiss_news'] = !$user_info['is_guest'] && allowedTo('can_dismiss_news'); $dismissed_items = isset($user_info['meta']['dismissed_news_items']) ? $user_info['meta']['dismissed_news_items'] : array(); $dismissed_item_count = count($dismissed_items); $cache_key = 'newsitems'; if (($cached_news = CacheAPI::getCache($cache_key, 1200)) == null) { $result = smf_db_query(' SELECT * FROM {db_prefix}news'); while ($row = mysql_fetch_assoc($result)) { $context['raw_news_items'][] = array('id' => $row['id_news'], 'teaser' => !empty($row['teaser']) ? parse_bbc($row['teaser']) : '', 'body' => parse_bbc($row['body']), 'groups' => explode(',', $row['groups']), 'on_index' => $row['on_index'] ? true : false, 'can_dismiss' => $row['can_dismiss'], 'topics' => explode(',', $row['topics']), 'boards' => explode(',', $row['boards'])); } mysql_free_result($result); if (count($context['raw_news_items']) > 0) { CacheAPI::putCache($cache_key, $context['raw_news_items'], 1200); } else { CacheAPI::putCache($cache_key, null, 0); } } else { $context['raw_news_items'] = $cached_news; } foreach ($context['raw_news_items'] as &$item) { if (0 == $board && 0 == $topic && !$item['on_index']) { continue; } if ($topic) { if (empty($item['topics'][0]) || !empty($item['boards'][0]) && !in_array($board, $item['boards'])) { continue; } if (!empty($item['topics'][0]) && $item['topics'][0] != -1 && !in_array($topic, $item['topics'])) { continue; } } if ($board) { if ($topic == 0 && !empty($item['topics'][0]) && $item['topics'][0] != -1 || !empty($item['boards'][0]) && !in_array($board, $item['boards'])) { continue; } } if (!empty($item['groups'][0]) && !in_array($user_info['groups'], $item['groups'])) { continue; } if ($context['can_dismiss_news'] && $item['can_dismiss'] && $dismissed_item_count && isset($dismissed_items[$item['id']])) { continue; } $context['news_items'][] =& $item; $context['news_item_count']++; } // we have news items to show, we need the template if ($context['news_item_count'] && !EoS_Smarty::isActive()) { loadTemplate('News'); } }
public function searchQuery($search_params, $searchWords, $excludedIndexWords, &$participants, &$searchArray) { global $modSettings, $context, $sourcedir, $user_info, $scripturl; if (($cached_results = CacheAPI::getCache('search_results_' . md5($user_info['query_see_board'] . '_' . $context['params']))) === null) { require_once $sourcedir . '/contrib/sphinxapi.php'; $mySphinx = new SphinxClient(); $mySphinx->SetServer($modSettings['sphinx_searchd_server'], (int) $modSettings['sphinx_searchd_port']); $mySphinx->SetLimits(0, (int) $modSettings['sphinx_max_results']); $mySphinx->SetMatchMode(SPH_MATCH_BOOLEAN); if (!$search_params['show_complete']) { $mySphinx->SetGroupBy('ID_TOPIC', SPH_GROUPBY_ATTR); } $mySphinx->SetSortMode($search_params['sort_dir'] === 'asc' ? SPH_SORT_ATTR_ASC : SPH_SORT_ATTR_DESC, $search_params['sort'] === 'ID_MSG' ? 'ID_TOPIC' : $search_params['sort']); if (!empty($search_params['topic'])) { $mySphinx->SetFilter('ID_TOPIC', array((int) $search_params['topic'])); } if (!empty($search_params['min_msg_id']) || !empty($search_params['max_msg_id'])) { $mySphinx->SetIDRange(empty($search_params['min_msg_id']) ? 0 : (int) $search_params['min_msg_id'], empty($search_params['max_msg_id']) ? (int) $modSettings['maxMsgID'] : (int) $search_params['max_msg_id']); } if (!empty($search_params['brd'])) { $mySphinx->SetFilter('ID_BOARD', $search_params['brd']); } if (!empty($search_params['prefix'])) { $mySphinx->SetFilter('ID_PREFIX', $search_params['prefix']); } if (!empty($search_params['memberlist'])) { $mySphinx->SetFilter('ID_MEMBER', $search_params['memberlist']); } $orResults = array(); foreach ($searchWords as $orIndex => $words) { $andResult = ''; foreach ($words['indexed_words'] as $sphinxWord) { $andResult .= (in_array($sphinxWord, $excludedIndexWords) ? '-' : '') . $sphinxWord . ' & '; } $orResults[] = substr($andResult, 0, -3); } $query = count($orResults) === 1 ? $orResults[0] : '(' . implode(') | (', $orResults) . ')'; // Execute the search query. $request = $mySphinx->Query($query, 'smf_index'); // Can a connection to the deamon be made? if ($request === false) { fatal_lang_error('error_no_search_daemon'); } // Get the relevant information from the search results. $cached_results = array('matches' => array(), 'num_results' => $request['total']); if (isset($request['matches'])) { foreach ($request['matches'] as $msgID => $match) { $cached_results['matches'][$msgID] = array('id' => $match['attrs']['id_topic'], 'relevance' => round($match['attrs']['relevance'] / 10000, 1) . '%', 'matches' => array()); if (!$search_params['show_complete']) { $cached_results['matches'][$msgID]['num_matches'] = $match['attrs']['@count']; } } } CacheAPI::putCache('search_results_' . md5($user_info['query_see_board']) . '_' . $context['params'], $cached_results, 600); } foreach (array_slice(array_keys($cached_results['matches']), $_REQUEST['start'], $modSettings['search_results_per_page']) as $msgID) { $context['topics'][$msgID] = $cached_results['matches'][$msgID]; $participants[$cached_results['matches'][$msgID]['id']] = false; } // Sentences need to be broken up in words for proper highlighting. foreach ($searchWords as $orIndex => $words) { $searchArray = array_merge($searchArray, $searchWords[$orIndex]['subject_words']); } // 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'], $cached_results['num_results'], $modSettings['search_results_per_page'], false); return $cached_results['num_results']; }
function cache_get_data($key, $ttl = 120) { return CacheAPI::getCache($key, $ttl); }
function MessagePost() { global $txt, $sourcedir, $scripturl, $modSettings; global $context, $options, $language, $user_info; isAllowedTo('pm_send'); loadLanguage('PersonalMessage'); // Just in case it was loaded from somewhere else. //loadTemplate('PersonalMessage'); //EoS_Smarty::loadTemplate('pm/send'); // Extract out the spam settings - cause it's neat. list($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']); // Set the title... $context['page_title'] = $txt['send_message']; $context['reply'] = isset($_REQUEST['pmsg']) || isset($_REQUEST['quote']); // Check whether we've gone over the limit of messages we can send per hour. 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 messages have they sent this last hour? $request = smf_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) = mysql_fetch_row($request); mysql_free_result($request); if (!empty($postCount) && $postCount >= $modSettings['pm_posts_per_hour']) { fatal_lang_error('pm_too_many_per_hour', true, array($modSettings['pm_posts_per_hour'])); } } // Quoting/Replying to a message? if (!empty($_REQUEST['pmsg'])) { $pmsg = (int) $_REQUEST['pmsg']; // Make sure this is yours. if (!isAccessiblePM($pmsg)) { fatal_lang_error('no_access', false); } // Work out whether this is one you've received? $request = smf_db_query(' SELECT id_pm FROM {db_prefix}pm_recipients WHERE id_pm = {int:id_pm} AND id_member = {int:current_member} LIMIT 1', array('current_member' => $user_info['id'], 'id_pm' => $pmsg)); $isReceived = mysql_num_rows($request) != 0; mysql_free_result($request); // Get the quoted message (and make sure you're allowed to see this quote!). $request = smf_db_query(' SELECT pm.id_pm, CASE WHEN pm.id_pm_head = {int:id_pm_head_empty} THEN pm.id_pm ELSE pm.id_pm_head END AS pm_head, pm.body, pm.subject, pm.msgtime, mem.member_name, IFNULL(mem.id_member, 0) AS id_member, IFNULL(mem.real_name, pm.from_name) AS real_name FROM {db_prefix}personal_messages AS pm' . (!$isReceived ? '' : ' INNER JOIN {db_prefix}pm_recipients AS pmr ON (pmr.id_pm = {int:id_pm})') . ' LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = pm.id_member_from) WHERE pm.id_pm = {int:id_pm}' . (!$isReceived ? ' AND pm.id_member_from = {int:current_member}' : ' AND pmr.id_member = {int:current_member}') . ' LIMIT 1', array('current_member' => $user_info['id'], 'id_pm_head_empty' => 0, 'id_pm' => $pmsg)); if (mysql_num_rows($request) == 0) { fatal_lang_error('pm_not_yours', false); } $row_quoted = mysql_fetch_assoc($request); mysql_free_result($request); // Censor the message. censorText($row_quoted['subject']); censorText($row_quoted['body']); // Add 'Re: ' to it.... if (!isset($context['response_prefix']) && !($context['response_prefix'] = CacheAPI::getCache('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'); } CacheAPI::putCache('response_prefix', $context['response_prefix'], 600); } $form_subject = $row_quoted['subject']; if ($context['reply'] && trim($context['response_prefix']) != '' && commonAPI::strpos($form_subject, trim($context['response_prefix'])) !== 0) { $form_subject = $context['response_prefix'] . $form_subject; } if (isset($_REQUEST['quote'])) { // Remove any nested quotes and <br />... $form_message = preg_replace('~<br ?/?' . '>~i', "\n", $row_quoted['body']); if (!empty($modSettings['removeNestedQuotes'])) { $form_message = preg_replace(array('~\\n?\\[quote.*?\\].+?\\[/quote\\]\\n?~is', '~^\\n~', '~\\[/quote\\]~'), '', $form_message); } if (empty($row_quoted['id_member'])) { $form_message = '[quote author="' . $row_quoted['real_name'] . '"]' . "\n" . $form_message . "\n" . '[/quote]'; } else { $form_message = '[quote author=' . $row_quoted['real_name'] . ' link=action=profile;u=' . $row_quoted['id_member'] . ' date=' . $row_quoted['msgtime'] . ']' . "\n" . $form_message . "\n" . '[/quote]'; } } else { $form_message = ''; } // Do the BBC thang on the message. $row_quoted['body'] = parse_bbc($row_quoted['body'], true, 'pm' . $row_quoted['id_pm']); // Set up the quoted message array. $context['quoted_message'] = array('id' => $row_quoted['id_pm'], 'pm_head' => $row_quoted['pm_head'], 'member' => array('name' => $row_quoted['real_name'], 'username' => $row_quoted['member_name'], 'id' => $row_quoted['id_member'], 'href' => !empty($row_quoted['id_member']) ? $scripturl . '?action=profile;u=' . $row_quoted['id_member'] : '', 'link' => !empty($row_quoted['id_member']) ? '<a href="' . $scripturl . '?action=profile;u=' . $row_quoted['id_member'] . '">' . $row_quoted['real_name'] . '</a>' : $row_quoted['real_name']), 'subject' => $row_quoted['subject'], 'time' => timeformat($row_quoted['msgtime']), 'timestamp' => forum_time(true, $row_quoted['msgtime']), 'body' => $row_quoted['body']); } else { $context['quoted_message'] = false; $form_subject = ''; $form_message = ''; } $context['recipients'] = array('to' => array(), 'bcc' => array()); // Sending by ID? Replying to all? Fetch the real_name(s). if (isset($_REQUEST['u'])) { // If the user is replying to all, get all the other members this was sent to.. if ($_REQUEST['u'] == 'all' && isset($row_quoted)) { // Firstly, to reply to all we clearly already have $row_quoted - so have the original member from. if ($row_quoted['id_member'] != $user_info['id']) { $context['recipients']['to'][] = array('id' => $row_quoted['id_member'], 'name' => htmlspecialchars($row_quoted['real_name'])); } // Now to get the others. $request = smf_db_query(' SELECT mem.id_member, mem.real_name FROM {db_prefix}pm_recipients AS pmr INNER JOIN {db_prefix}members AS mem ON (mem.id_member = pmr.id_member) WHERE pmr.id_pm = {int:id_pm} AND pmr.id_member != {int:current_member} AND pmr.bcc = {int:not_bcc}', array('current_member' => $user_info['id'], 'id_pm' => $pmsg, 'not_bcc' => 0)); while ($row = mysql_fetch_assoc($request)) { $context['recipients']['to'][] = array('id' => $row['id_member'], 'name' => $row['real_name']); } mysql_free_result($request); } else { $_REQUEST['u'] = explode(',', $_REQUEST['u']); foreach ($_REQUEST['u'] as $key => $uID) { $_REQUEST['u'][$key] = (int) $uID; } $_REQUEST['u'] = array_unique($_REQUEST['u']); $request = smf_db_query(' SELECT id_member, real_name FROM {db_prefix}members WHERE id_member IN ({array_int:member_list}) LIMIT ' . count($_REQUEST['u']), array('member_list' => $_REQUEST['u'])); while ($row = mysql_fetch_assoc($request)) { $context['recipients']['to'][] = array('id' => $row['id_member'], 'name' => $row['real_name']); } mysql_free_result($request); } // Get a literal name list in case the user has JavaScript disabled. $names = array(); foreach ($context['recipients']['to'] as $to) { $names[] = $to['name']; } $context['to_value'] = empty($names) ? '' : '"' . implode('", "', $names) . '"'; } else { $context['to_value'] = ''; } // Set the defaults... $context['subject'] = $form_subject != '' ? $form_subject : $txt['no_subject']; $context['message'] = str_replace(array('"', '<', '>', ' '), array('"', '<', '>', ' '), $form_message); $context['post_error'] = array(); $context['copy_to_outbox'] = !empty($options['copy_to_outbox']); // And build the link tree. $context['linktree'][] = array('url' => $scripturl . '?action=pm;sa=send', 'name' => $txt['new_message']); $modSettings['disable_wysiwyg'] = !empty($modSettings['disable_wysiwyg']) || empty($modSettings['enableBBC']); // Needed for the WYSIWYG editor. require_once $sourcedir . '/lib/Subs-Editor.php'; // Now create the editor. $editorOptions = array('id' => 'message', 'value' => $context['message'], 'height' => (isset($options['editor_height']) && $options['editor_height'] > 150 && $options['editor_height'] < 800 ? $options['editor_height'] : 250) . 'px', 'rows' => 10, 'width' => '100%', 'labels' => array('post_button' => $txt['send_message'])); create_control_richedit($editorOptions); // Store the ID for old compatibility. $context['post_box_name'] = $editorOptions['id']; $context['bcc_value'] = ''; $context['require_verification'] = !$user_info['is_admin'] && !empty($modSettings['pm_posts_verification']) && $user_info['posts'] < $modSettings['pm_posts_verification']; if ($context['require_verification']) { $verificationOptions = array('id' => 'pm', 'skip_template' => true); $context['require_verification'] = create_control_verification($verificationOptions); $context['visual_verification_id'] = $verificationOptions['id']; } // Register this form and get a sequence number in $context. checkSubmitOnce('register'); }
function MergeExecute($topics = array()) { global $user_info, $txt, $context, $scripturl, $sourcedir; global $smcFunc, $language, $modSettings; // Check the session. checkSession('request'); // Handle URLs from MergeIndex. if (!empty($_GET['from']) && !empty($_GET['to'])) { $topics = array((int) $_GET['from'], (int) $_GET['to']); } // If we came from a form, the topic IDs came by post. if (!empty($_POST['topics']) && is_array($_POST['topics'])) { $topics = $_POST['topics']; } // There's nothing to merge with just one topic... if (empty($topics) || !is_array($topics) || count($topics) == 1) { fatal_lang_error('merge_need_more_topics'); } // Make sure every topic is numeric, or some nasty things could be done with the DB. foreach ($topics as $id => $topic) { $topics[$id] = (int) $topic; } // Joy of all joys, make sure they're not pi**ing about with unapproved topics they can't see :P if ($modSettings['postmod_active']) { $can_approve_boards = boardsAllowedTo('approve_posts'); } // Get info about the topics and polls that will be merged. $request = smf_db_query(' SELECT t.id_topic, t.id_board, t.id_poll, t.num_views, t.is_sticky, t.approved, t.num_replies, t.unapproved_posts, m1.subject, m1.poster_time AS time_started, IFNULL(mem1.id_member, 0) AS id_member_started, IFNULL(mem1.real_name, m1.poster_name) AS name_started, m2.poster_time AS time_updated, IFNULL(mem2.id_member, 0) AS id_member_updated, IFNULL(mem2.real_name, m2.poster_name) AS name_updated FROM {db_prefix}topics AS t INNER JOIN {db_prefix}messages AS m1 ON (m1.id_msg = t.id_first_msg) INNER JOIN {db_prefix}messages AS m2 ON (m2.id_msg = t.id_last_msg) LEFT JOIN {db_prefix}members AS mem1 ON (mem1.id_member = m1.id_member) LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = m2.id_member) WHERE t.id_topic IN ({array_int:topic_list}) ORDER BY t.id_first_msg LIMIT ' . count($topics), array('topic_list' => $topics)); if (mysql_num_rows($request) < 2) { fatal_lang_error('no_topic_id'); } $num_views = 0; $is_sticky = 0; $boardTotals = array(); $boards = array(); $polls = array(); while ($row = mysql_fetch_assoc($request)) { // Make a note for the board counts... if (!isset($boardTotals[$row['id_board']])) { $boardTotals[$row['id_board']] = array('posts' => 0, 'topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0); } // We can't see unapproved topics here? if ($modSettings['postmod_active'] && !$row['approved'] && $can_approve_boards != array(0) && in_array($row['id_board'], $can_approve_boards)) { continue; } elseif (!$row['approved']) { $boardTotals[$row['id_board']]['unapproved_topics']++; } else { $boardTotals[$row['id_board']]['topics']++; } $boardTotals[$row['id_board']]['unapproved_posts'] += $row['unapproved_posts']; $boardTotals[$row['id_board']]['posts'] += $row['num_replies'] + ($row['approved'] ? 1 : 0); $topic_data[$row['id_topic']] = array('id' => $row['id_topic'], 'board' => $row['id_board'], 'poll' => $row['id_poll'], 'num_views' => $row['num_views'], 'subject' => $row['subject'], 'started' => array('time' => timeformat($row['time_started']), 'timestamp' => forum_time(true, $row['time_started']), 'href' => empty($row['id_member_started']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_started'], 'link' => empty($row['id_member_started']) ? $row['name_started'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_started'] . '">' . $row['name_started'] . '</a>'), 'updated' => array('time' => timeformat($row['time_updated']), 'timestamp' => forum_time(true, $row['time_updated']), 'href' => empty($row['id_member_updated']) ? '' : $scripturl . '?action=profile;u=' . $row['id_member_updated'], 'link' => empty($row['id_member_updated']) ? $row['name_updated'] : '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member_updated'] . '">' . $row['name_updated'] . '</a>')); $num_views += $row['num_views']; $boards[] = $row['id_board']; // If there's no poll, id_poll == 0... if ($row['id_poll'] > 0) { $polls[] = $row['id_poll']; } // Store the id_topic with the lowest id_first_msg. if (empty($firstTopic)) { $firstTopic = $row['id_topic']; } $is_sticky = max($is_sticky, $row['is_sticky']); } mysql_free_result($request); // If we didn't get any topics then they've been messing with unapproved stuff. if (empty($topic_data)) { fatal_lang_error('no_topic_id'); } $boards = array_values(array_unique($boards)); // The parameters of MergeExecute were set, so this must've been an internal call. if (!empty($topics)) { isAllowedTo('merge_any', $boards); loadTemplate('SplitTopics'); } // Get the boards a user is allowed to merge in. $merge_boards = boardsAllowedTo('merge_any'); if (empty($merge_boards)) { fatal_lang_error('cannot_merge_any', 'user'); } // Make sure they can see all boards.... $request = smf_db_query(' SELECT b.id_board FROM {db_prefix}boards AS b WHERE b.id_board IN ({array_int:boards}) AND {query_see_board}' . (!in_array(0, $merge_boards) ? ' AND b.id_board IN ({array_int:merge_boards})' : '') . ' LIMIT ' . count($boards), array('boards' => $boards, 'merge_boards' => $merge_boards)); // If the number of boards that's in the output isn't exactly the same as we've put in there, you're in trouble. if (mysql_num_rows($request) != count($boards)) { fatal_lang_error('no_board'); } mysql_free_result($request); if (empty($_REQUEST['sa']) || $_REQUEST['sa'] == 'options') { if (count($polls) > 1) { $request = smf_db_query(' SELECT t.id_topic, t.id_poll, m.subject, p.question FROM {db_prefix}polls AS p INNER JOIN {db_prefix}topics AS t ON (t.id_poll = p.id_poll) INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) WHERE p.id_poll IN ({array_int:polls}) LIMIT ' . count($polls), array('polls' => $polls)); while ($row = mysql_fetch_assoc($request)) { $context['polls'][] = array('id' => $row['id_poll'], 'topic' => array('id' => $row['id_topic'], 'subject' => $row['subject']), 'question' => $row['question'], 'selected' => $row['id_topic'] == $firstTopic); } mysql_free_result($request); } if (count($boards) > 1) { $request = smf_db_query(' SELECT id_board, name FROM {db_prefix}boards WHERE id_board IN ({array_int:boards}) ORDER BY name LIMIT ' . count($boards), array('boards' => $boards)); while ($row = mysql_fetch_assoc($request)) { $context['boards'][] = array('id' => $row['id_board'], 'name' => $row['name'], 'selected' => $row['id_board'] == $topic_data[$firstTopic]['board']); } mysql_free_result($request); } $context['topics'] = $topic_data; foreach ($topic_data as $id => $topic) { $context['topics'][$id]['selected'] = $topic['id'] == $firstTopic; } $context['page_title'] = $txt['merge']; $context['sub_template'] = 'merge_extra_options'; return; } // Determine target board. $target_board = count($boards) > 1 ? (int) $_REQUEST['board'] : $boards[0]; if (!in_array($target_board, $boards)) { fatal_lang_error('no_board'); } // Determine which poll will survive and which polls won't. $target_poll = count($polls) > 1 ? (int) $_POST['poll'] : (count($polls) == 1 ? $polls[0] : 0); if ($target_poll > 0 && !in_array($target_poll, $polls)) { fatal_lang_error('no_access', false); } $deleted_polls = empty($target_poll) ? $polls : array_diff($polls, array($target_poll)); // Determine the subject of the newly merged topic - was a custom subject specified? if (empty($_POST['subject']) && isset($_POST['custom_subject']) && $_POST['custom_subject'] != '') { $target_subject = strtr(commonAPI::htmltrim(commonAPI::htmlspecialchars($_POST['custom_subject'])), array("\r" => '', "\n" => '', "\t" => '')); // Keep checking the length. if (commonAPI::strlen($target_subject) > 100) { $target_subject = commonAPI::substr($target_subject, 0, 100); } // Nothing left - odd but pick the first topics subject. if ($target_subject == '') { $target_subject = $topic_data[$firstTopic]['subject']; } } elseif (!empty($topic_data[(int) $_POST['subject']]['subject'])) { $target_subject = $topic_data[(int) $_POST['subject']]['subject']; } else { $target_subject = $topic_data[$firstTopic]['subject']; } // Get the first and last message and the number of messages.... $request = smf_db_query(' SELECT approved, MIN(id_msg) AS first_msg, MAX(id_msg) AS last_msg, COUNT(*) AS message_count FROM {db_prefix}messages WHERE id_topic IN ({array_int:topics}) GROUP BY approved ORDER BY approved DESC', array('topics' => $topics)); $topic_approved = 1; while ($row = mysql_fetch_assoc($request)) { // If this is approved, or is fully unapproved. if ($row['approved'] || !isset($first_msg)) { $first_msg = $row['first_msg']; $last_msg = $row['last_msg']; if ($row['approved']) { $num_replies = $row['message_count'] - 1; $num_unapproved = 0; } else { $topic_approved = 0; $num_replies = 0; $num_unapproved = $row['message_count']; } } else { // If this has a lower first_msg then the first post is not approved and hence the number of replies was wrong! if ($first_msg > $row['first_msg']) { $first_msg = $row['first_msg']; $num_replies++; $topic_approved = 0; } $num_unapproved = $row['message_count']; } } mysql_free_result($request); // Ensure we have a board stat for the target board. if (!isset($boardTotals[$target_board])) { $boardTotals[$target_board] = array('posts' => 0, 'topics' => 0, 'unapproved_posts' => 0, 'unapproved_topics' => 0); } // Fix the topic count stuff depending on what the new one counts as. if ($topic_approved) { $boardTotals[$target_board]['topics']--; } else { $boardTotals[$target_board]['unapproved_topics']--; } $boardTotals[$target_board]['unapproved_posts'] -= $num_unapproved; $boardTotals[$target_board]['posts'] -= $topic_approved ? $num_replies + 1 : $num_replies; // Get the member ID of the first and last message. $request = smf_db_query(' SELECT id_member FROM {db_prefix}messages WHERE id_msg IN ({int:first_msg}, {int:last_msg}) ORDER BY id_msg LIMIT 2', array('first_msg' => $first_msg, 'last_msg' => $last_msg)); list($member_started) = mysql_fetch_row($request); list($member_updated) = mysql_fetch_row($request); // First and last message are the same, so only row was returned. if ($member_updated === NULL) { $member_updated = $member_started; } mysql_free_result($request); // Assign the first topic ID to be the merged topic. $id_topic = min($topics); // Delete the remaining topics. $deleted_topics = array_diff($topics, array($id_topic)); smf_db_query(' DELETE FROM {db_prefix}topics WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics)); smf_db_query(' DELETE FROM {db_prefix}log_search_subjects WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics)); // Asssign the properties of the newly merged topic. smf_db_query(' UPDATE {db_prefix}topics SET id_board = {int:id_board}, id_member_started = {int:id_member_started}, id_member_updated = {int:id_member_updated}, id_first_msg = {int:id_first_msg}, id_last_msg = {int:id_last_msg}, id_poll = {int:id_poll}, num_replies = {int:num_replies}, unapproved_posts = {int:unapproved_posts}, num_views = {int:num_views}, is_sticky = {int:is_sticky}, approved = {int:approved} WHERE id_topic = {int:id_topic}', array('id_board' => $target_board, 'is_sticky' => $is_sticky, 'approved' => $topic_approved, 'id_topic' => $id_topic, 'id_member_started' => $member_started, 'id_member_updated' => $member_updated, 'id_first_msg' => $first_msg, 'id_last_msg' => $last_msg, 'id_poll' => $target_poll, 'num_replies' => $num_replies, 'unapproved_posts' => $num_unapproved, 'num_views' => $num_views)); // Grab the response prefix (like 'Re: ') in the default forum language. if (!isset($context['response_prefix']) && !($context['response_prefix'] = CacheAPI::getCache('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'); } CacheAPI::putCache('response_prefix', $context['response_prefix'], 600); } // Change the topic IDs of all messages that will be merged. Also adjust subjects if 'enforce subject' was checked. smf_db_query(' UPDATE {db_prefix}messages SET id_topic = {int:id_topic}, id_board = {int:target_board}' . (empty($_POST['enforce_subject']) ? '' : ', subject = {string:subject}') . ' WHERE id_topic IN ({array_int:topic_list})', array('topic_list' => $topics, 'id_topic' => $id_topic, 'target_board' => $target_board, 'subject' => $context['response_prefix'] . $target_subject)); // Any reported posts should reflect the new board. smf_db_query(' UPDATE {db_prefix}log_reported SET id_topic = {int:id_topic}, id_board = {int:target_board} WHERE id_topic IN ({array_int:topics_list})', array('topics_list' => $topics, 'id_topic' => $id_topic, 'target_board' => $target_board)); // Change the subject of the first message... smf_db_query(' UPDATE {db_prefix}messages SET subject = {string:target_subject} WHERE id_msg = {int:first_msg}', array('first_msg' => $first_msg, 'target_subject' => $target_subject)); // Adjust all calendar events to point to the new topic. smf_db_query(' UPDATE {db_prefix}calendar SET id_topic = {int:id_topic}, id_board = {int:target_board} WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics, 'id_topic' => $id_topic, 'target_board' => $target_board)); // Merge log topic entries. $request = smf_db_query(' SELECT id_member, MIN(id_msg) AS new_id_msg FROM {db_prefix}log_topics WHERE id_topic IN ({array_int:topics}) GROUP BY id_member', array('topics' => $topics)); if (mysql_num_rows($request) > 0) { $replaceEntries = array(); while ($row = mysql_fetch_assoc($request)) { $replaceEntries[] = array($row['id_member'], $id_topic, $row['new_id_msg']); } smf_db_insert('replace', '{db_prefix}log_topics', array('id_member' => 'int', 'id_topic' => 'int', 'id_msg' => 'int'), $replaceEntries, array('id_member', 'id_topic')); unset($replaceEntries); // Get rid of the old log entries. smf_db_query(' DELETE FROM {db_prefix}log_topics WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics)); } mysql_free_result($request); // Merge topic notifications. $notifications = isset($_POST['notifications']) && is_array($_POST['notifications']) ? array_intersect($topics, $_POST['notifications']) : array(); if (!empty($notifications)) { $request = smf_db_query(' SELECT id_member, MAX(sent) AS sent FROM {db_prefix}log_notify WHERE id_topic IN ({array_int:topics_list}) GROUP BY id_member', array('topics_list' => $notifications)); if (mysql_num_rows($request) > 0) { $replaceEntries = array(); while ($row = mysql_fetch_assoc($request)) { $replaceEntries[] = array($row['id_member'], $id_topic, 0, $row['sent']); } smf_db_insert('replace', '{db_prefix}log_notify', array('id_member' => 'int', 'id_topic' => 'int', 'id_board' => 'int', 'sent' => 'int'), $replaceEntries, array('id_member', 'id_topic', 'id_board')); unset($replaceEntries); smf_db_query(' DELETE FROM {db_prefix}log_topics WHERE id_topic IN ({array_int:deleted_topics})', array('deleted_topics' => $deleted_topics)); } mysql_free_result($request); } // Get rid of the redundant polls. if (!empty($deleted_polls)) { smf_db_query(' DELETE FROM {db_prefix}polls WHERE id_poll IN ({array_int:deleted_polls})', array('deleted_polls' => $deleted_polls)); smf_db_query(' DELETE FROM {db_prefix}poll_choices WHERE id_poll IN ({array_int:deleted_polls})', array('deleted_polls' => $deleted_polls)); smf_db_query(' DELETE FROM {db_prefix}log_polls WHERE id_poll IN ({array_int:deleted_polls})', array('deleted_polls' => $deleted_polls)); } // Cycle through each board... foreach ($boardTotals as $id_board => $stats) { smf_db_query(' UPDATE {db_prefix}boards SET num_topics = CASE WHEN {int:topics} > num_topics THEN 0 ELSE num_topics - {int:topics} END, unapproved_topics = CASE WHEN {int:unapproved_topics} > unapproved_topics THEN 0 ELSE unapproved_topics - {int:unapproved_topics} END, num_posts = CASE WHEN {int:posts} > num_posts THEN 0 ELSE num_posts - {int:posts} END, unapproved_posts = CASE WHEN {int:unapproved_posts} > unapproved_posts THEN 0 ELSE unapproved_posts - {int:unapproved_posts} END WHERE id_board = {int:id_board}', array('id_board' => $id_board, 'topics' => $stats['topics'], 'unapproved_topics' => $stats['unapproved_topics'], 'posts' => $stats['posts'], 'unapproved_posts' => $stats['unapproved_posts'])); } // Determine the board the final topic resides in $request = smf_db_query(' SELECT id_board FROM {db_prefix}topics WHERE id_topic = {int:id_topic} LIMIT 1', array('id_topic' => $id_topic)); list($id_board) = mysql_fetch_row($request); mysql_free_result($request); require_once $sourcedir . '/lib/Subs-Post.php'; // Update all the statistics. updateStats('topic'); updateStats('subject', $id_topic, $target_subject); updateLastMessages($boards); logAction('merge', array('topic' => $id_topic, 'board' => $id_board)); // Notify people that these topics have been merged? sendNotifications($id_topic, 'merge'); // Send them to the all done page. redirectexit('action=mergetopics;sa=done;to=' . $id_topic . ';targetboard=' . $target_board); }
function ModBlockReportedPosts() { global $context, $user_info, $scripturl, $smcFunc; // Got the info already? $cachekey = md5(serialize($user_info['mod_cache']['bq'])); $context['reported_posts'] = array(); if ($user_info['mod_cache']['bq'] == '0=1') { return 'reported_posts_block'; } if (($reported_posts = CacheAPI::getCache('reported_posts_' . $cachekey, 90)) === null) { // By George, that means we in a position to get the reports, jolly good. $request = smf_db_query(' SELECT lr.id_report, lr.id_msg, lr.id_topic, lr.id_board, lr.id_member, lr.subject, lr.num_reports, IFNULL(mem.real_name, lr.membername) AS author_name, IFNULL(mem.id_member, 0) AS id_author FROM {db_prefix}log_reported AS lr LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = lr.id_member) WHERE ' . ($user_info['mod_cache']['bq'] == '1=1' || $user_info['mod_cache']['bq'] == '0=1' ? $user_info['mod_cache']['bq'] : 'lr.' . $user_info['mod_cache']['bq']) . ' AND lr.closed = {int:not_closed} AND lr.ignore_all = {int:not_ignored} ORDER BY lr.time_updated DESC LIMIT 10', array('not_closed' => 0, 'not_ignored' => 0)); $reported_posts = array(); while ($row = mysql_fetch_assoc($request)) { $reported_posts[] = $row; } mysql_free_result($request); // Cache it. CacheAPI::putCache('reported_posts_' . $cachekey, $reported_posts, 90); } $context['reported_posts'] = array(); foreach ($reported_posts as $i => $row) { $context['reported_posts'][] = array('id' => $row['id_report'], 'alternate' => $i % 2, 'topic_href' => $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'], 'report_href' => $scripturl . '?action=moderate;area=reports;report=' . $row['id_report'], 'author' => array('id' => $row['id_author'], 'name' => $row['author_name'], 'link' => $row['id_author'] ? '<a href="' . $scripturl . '?action=profile;u=' . $row['id_author'] . '">' . $row['author_name'] . '</a>' : $row['author_name'], 'href' => $scripturl . '?action=profile;u=' . $row['id_author']), 'comments' => array(), 'subject' => $row['subject'], 'num_reports' => $row['num_reports']); } return 'reported_posts'; }
function createWaveFile($word) { global $settings, $user_info, $context; // Allow max 2 requests per 20 seconds. if (($ip = CacheAPI::getCache('wave_file/' . $user_info['ip'], 20)) > 2 || ($ip2 = CacheAPI::getCache('wave_file/' . $user_info['ip2'], 20)) > 2) { die(header('HTTP/1.1 400 Bad Request')); } CacheAPI::putCache('wave_file/' . $user_info['ip'], $ip ? $ip + 1 : 1, 20); CacheAPI::putCache('wave_file/' . $user_info['ip2'], $ip2 ? $ip2 + 1 : 1, 20); // Fixate randomization for this word. mt_srand(end(unpack('n', md5($word . session_id())))); // Try to see if there's a sound font in the user's language. if (file_exists($settings['default_theme_dir'] . '/fonts/sound/a.' . $user_info['language'] . '.wav')) { $sound_language = $user_info['language']; } elseif (file_exists($settings['default_theme_dir'] . '/fonts/sound/a.english.wav')) { $sound_language = 'english'; } else { return false; } // File names are in lower case so lets make sure that we are only using a lower case string $word = strtolower($word); // Loop through all letters of the word $word. $sound_word = ''; for ($i = 0; $i < strlen($word); $i++) { $sound_letter = implode('', file($settings['default_theme_dir'] . '/fonts/sound/' . $word[$i] . '.' . $sound_language . '.wav')); if (strpos($sound_letter, 'data') === false) { return false; } $sound_letter = substr($sound_letter, strpos($sound_letter, 'data') + 8); switch ($word[$i] === 's' ? 0 : mt_rand(0, 2)) { case 0: for ($j = 0, $n = strlen($sound_letter); $j < $n; $j++) { for ($k = 0, $m = round(mt_rand(15, 25) / 10); $k < $m; $k++) { $sound_word .= $word[$i] === 's' ? $sound_letter[$j] : chr(mt_rand(max(ord($sound_letter[$j]) - 1, 0x0), min(ord($sound_letter[$j]) + 1, 0xff))); } } break; case 1: for ($j = 0, $n = strlen($sound_letter) - 1; $j < $n; $j += 2) { $sound_word .= (mt_rand(0, 3) == 0 ? '' : $sound_letter[$j]) . (mt_rand(0, 3) === 0 ? $sound_letter[$j + 1] : $sound_letter[$j]) . (mt_rand(0, 3) === 0 ? $sound_letter[$j] : $sound_letter[$j + 1]) . $sound_letter[$j + 1] . (mt_rand(0, 3) == 0 ? $sound_letter[$j + 1] : ''); } $sound_word .= str_repeat($sound_letter[$n], 2); break; case 2: $shift = 0; for ($j = 0, $n = strlen($sound_letter); $j < $n; $j++) { if (mt_rand(0, 10) === 0) { $shift += mt_rand(-3, 3); } for ($k = 0, $m = round(mt_rand(15, 25) / 10); $k < $m; $k++) { $sound_word .= chr(min(max(ord($sound_letter[$j]) + $shift, 0x0), 0xff)); } } break; } $sound_word .= str_repeat(chr(0x80), mt_rand(10000, 10500)); } $data_size = strlen($sound_word); $file_size = $data_size + 0x24; $sample_rate = 16000; // Disable compression. ob_end_clean(); header('Content-Encoding: none'); // Output the wav. header('Content-type: audio/x-wav'); header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT'); header('Content-Length: ' . ($file_size + 0x8)); echo pack('nnVnnnnnnnnVVnnnnV', 0x5249, 0x4646, $file_size, 0x5741, 0x5645, 0x666d, 0x7420, 0x1000, 0x0, 0x100, 0x100, $sample_rate, $sample_rate, 0x100, 0x800, 0x6461, 0x7461, $data_size), $sound_word; // Noting more to add. die; }
function RecentPosts() { global $sourcedir, $txt, $scripturl, $user_info, $context, $modSettings, $board, $memberContext; if (!empty($modSettings['karmaMode'])) { require_once $sourcedir . '/lib/Subs-Ratings.php'; $boards_like_see = boardsAllowedTo('like_see'); $boards_like_give = boardsAllowedTo('like_give'); } else { $context['can_see_like'] = $context['can_give_like'] = false; $boards_like_see = array(); $boards_like_give = array(); } $context['time_now'] = time(); $context['need_synhlt'] = true; EoS_Smarty::loadTemplate('recent'); $context['template_functions'] = 'recentposts'; $context['messages_per_page'] = $modSettings['defaultMaxMessages']; $context['page_number'] = isset($_REQUEST['start']) ? $_REQUEST['start'] / $context['messages_per_page'] : 0; $context['page_title'] = $txt['recent_posts'] . ((int) $context['page_number'] > 0 ? ' - ' . $txt['page'] . ' ' . ($context['page_number'] + 1) : ''); $boards_hidden_1 = boardsAllowedTo('see_hidden1'); $boards_hidden_2 = boardsAllowedTo('see_hidden2'); $boards_hidden_3 = boardsAllowedTo('see_hidden3'); if (isset($_REQUEST['start']) && $_REQUEST['start'] > 95) { $_REQUEST['start'] = 95; } $query_parameters = array(); if (!empty($_REQUEST['c']) && empty($board)) { $_REQUEST['c'] = explode(',', $_REQUEST['c']); foreach ($_REQUEST['c'] as $i => $c) { $_REQUEST['c'][$i] = (int) $c; } if (count($_REQUEST['c']) == 1) { $request = smf_db_query(' SELECT name FROM {db_prefix}categories WHERE id_cat = {int:id_cat} LIMIT 1', array('id_cat' => $_REQUEST['c'][0])); list($name) = mysql_fetch_row($request); mysql_free_result($request); if (empty($name)) { fatal_lang_error('no_access', false); } $context['linktree'][] = array('url' => $scripturl . '#c' . (int) $_REQUEST['c'], 'name' => $name); } $request = smf_db_query(' SELECT b.id_board, b.num_posts FROM {db_prefix}boards AS b WHERE b.id_cat IN ({array_int:category_list}) AND {query_see_board}', array('category_list' => $_REQUEST['c'])); $total_cat_posts = 0; $boards = array(); while ($row = mysql_fetch_assoc($request)) { $boards[] = $row['id_board']; $total_cat_posts += $row['num_posts']; } mysql_free_result($request); if (empty($boards)) { fatal_lang_error('error_no_boards_selected'); } $query_this_board = 'b.id_board IN ({array_int:boards})'; $query_parameters['boards'] = $boards; // If this category has a significant number of posts in it... if ($total_cat_posts > 100 && $total_cat_posts > $modSettings['totalMessages'] / 15) { $query_this_board .= ' AND m.id_msg >= {int:max_id_msg}'; $query_parameters['max_id_msg'] = max(0, $modSettings['maxMsgID'] - 400 - $_REQUEST['start'] * 7); } $context['page_index'] = $total_cat_posts ? constructPageIndex($scripturl . '?action=recent;c=' . implode(',', $_REQUEST['c']), $_REQUEST['start'], min(100, $total_cat_posts), $context['messages_per_page'], false) : ''; } elseif (!empty($_REQUEST['boards'])) { $_REQUEST['boards'] = explode(',', $_REQUEST['boards']); foreach ($_REQUEST['boards'] as $i => $b) { $_REQUEST['boards'][$i] = (int) $b; } $request = smf_db_query(' SELECT b.id_board, b.num_posts FROM {db_prefix}boards AS b WHERE b.id_board IN ({array_int:board_list}) AND {query_see_board} LIMIT {int:limit}', array('board_list' => $_REQUEST['boards'], 'limit' => count($_REQUEST['boards']))); $total_posts = 0; $boards = array(); while ($row = mysql_fetch_assoc($request)) { $boards[] = $row['id_board']; $total_posts += $row['num_posts']; } mysql_free_result($request); if (empty($boards)) { fatal_lang_error('error_no_boards_selected'); } $query_this_board = 'b.id_board IN ({array_int:boards})'; $query_parameters['boards'] = $boards; // If these boards have a significant number of posts in them... if ($total_posts > 100 && $total_posts > $modSettings['totalMessages'] / 12) { $query_this_board .= ' AND m.id_msg >= {int:max_id_msg}'; $query_parameters['max_id_msg'] = max(0, $modSettings['maxMsgID'] - 500 - $_REQUEST['start'] * 9); } $context['page_index'] = $total_posts ? constructPageIndex($scripturl . '?action=recent;boards=' . implode(',', $_REQUEST['boards']), $_REQUEST['start'], min(100, $total_posts), $context['messages_per_page'], false) : ''; } elseif (!empty($board)) { $request = smf_db_query(' SELECT num_posts FROM {db_prefix}boards WHERE id_board = {int:current_board} LIMIT 1', array('current_board' => $board)); list($total_posts) = mysql_fetch_row($request); mysql_free_result($request); $query_this_board = 'b.id_board = {int:board}'; $query_parameters['board'] = $board; // If this board has a significant number of posts in it... if ($total_posts > 80 && $total_posts > $modSettings['totalMessages'] / 10) { $query_this_board .= ' AND m.id_msg >= {int:max_id_msg}'; $query_parameters['max_id_msg'] = max(0, $modSettings['maxMsgID'] - 600 - $_REQUEST['start'] * 10); } $context['page_index'] = $total_posts ? constructPageIndex($scripturl . '?action=recent;board=' . $board . '.%1$d', $_REQUEST['start'], min(100, $total_posts), $context['messages_per_page'], true) : ''; } else { $query_this_board = '{query_wanna_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' AND b.id_board != {int:recycle_board}' : '') . ' AND m.id_msg >= {int:max_id_msg}'; $query_parameters['max_id_msg'] = max(0, $modSettings['maxMsgID'] - 100 - $_REQUEST['start'] * 6); $query_parameters['recycle_board'] = $modSettings['recycle_board']; // !!! This isn't accurate because we ignore the recycle bin. $context['page_index'] = constructPageIndex($scripturl . '?action=recent', $_REQUEST['start'], min(100, $modSettings['totalMessages']), $context['messages_per_page'], false); } $context['linktree'][] = array('url' => $scripturl . '?action=recent' . (empty($board) ? empty($_REQUEST['c']) ? '' : ';c=' . (int) $_REQUEST['c'] : ';board=' . $board . '.0'), 'name' => $context['page_title']); $context['start'] = isset($_REQUEST['start']) ? $_REQUEST['start'] : 0; $key = 'recent-' . $user_info['id'] . '-' . md5(serialize(array_diff_key($query_parameters, array('max_id_msg' => 0)))) . '-' . (int) $_REQUEST['start']; if (empty($modSettings['cache_enable']) || ($messages = CacheAPI::getCache($key, 120)) == null) { $done = false; while (!$done) { // Find the 10 most recent messages they can *view*. // !!!SLOW This query is really slow still, probably? $request = smf_db_query(' SELECT m.id_msg FROM {db_prefix}messages AS m INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) WHERE ' . $query_this_board . ' AND m.approved = {int:is_approved} ORDER BY m.id_msg DESC LIMIT {int:offset}, {int:limit}', array_merge($query_parameters, array('is_approved' => 1, 'offset' => $_REQUEST['start'], 'limit' => $context['messages_per_page']))); // If we don't have 10 results, try again with an unoptimized version covering all rows, and cache the result. if (isset($query_parameters['max_id_msg']) && mysql_num_rows($request) < $context['messages_per_page']) { mysql_free_result($request); $query_this_board = str_replace('AND m.id_msg >= {int:max_id_msg}', '', $query_this_board); $cache_results = true; unset($query_parameters['max_id_msg']); } else { $done = true; } } $messages = array(); while ($row = mysql_fetch_assoc($request)) { $messages[] = $row['id_msg']; } mysql_free_result($request); if (!empty($cache_results)) { CacheAPI::putCache($key, $messages, 120); } } // Nothing here... Or at least, nothing you can see... if (empty($messages)) { $context['posts'] = array(); return; } // Get all the most recent posts. $request = smf_db_query(' SELECT m.id_msg, m.subject, m.smileys_enabled, m.poster_time, m.modified_time, m.body, m.icon, m.id_topic, t.id_board, b.id_cat, mc.body AS cached_body, b.name AS bname, c.name AS cname, t.num_replies, m.id_member, m2.id_member AS id_first_member, ' . (!empty($modSettings['karmaMode']) ? 'lc.likes_count, lc.like_status, lc.updated AS like_updated, l.rtype AS liked, ' : '0 AS likes_count, 0 AS like_status, 0 AS like_updated, 0 AS liked, ') . ' IFNULL(mem2.real_name, m2.poster_name) AS first_poster_name, t.id_first_msg, m2.subject AS first_subject, m2.poster_time AS time_started, IFNULL(mem.real_name, m.poster_name) AS poster_name, t.id_last_msg 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 m2 ON (m2.id_msg = t.id_first_msg) LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member) LEFT JOIN {db_prefix}members AS mem2 ON (mem2.id_member = m2.id_member)' . (!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 lc ON (lc.id_msg = m.id_msg AND lc.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}) WHERE m.id_msg IN ({array_int:message_list}) ORDER BY m.id_msg DESC LIMIT ' . count($messages), array('message_list' => $messages, 'style' => $user_info['smiley_set_id'], 'lang' => $user_info['language_id'], 'id_user' => $user_info['id'])); $counter = $_REQUEST['start'] + 1; $context['posts'] = array(); $board_ids = array('own' => array(), 'any' => array()); $userids = array(); while ($row = mysql_fetch_assoc($request)) { $check_boards = array(0, $row['id_board']); // 0 is for admin $context['can_see_hidden_level1'] = count(array_intersect($check_boards, $boards_hidden_1)) > 0; $context['can_see_hidden_level2'] = count(array_intersect($check_boards, $boards_hidden_2)) > 0; $context['can_see_hidden_level3'] = count(array_intersect($check_boards, $boards_hidden_3)) > 0; $context['can_see_like'] = count(array_intersect($check_boards, $boards_like_see)) > 0; $context['can_give_like'] = count(array_intersect($check_boards, $boards_like_give)) > 0; // Censor everything. censorText($row['body']); censorText($row['subject']); getCachedPost($row); // this will also care about bbc parsing... // And build the array. $thref = URL::topic($row['id_topic'], $row['first_subject'], 0, false, '.msg' . $row['id_msg'], '#' . $row['id_msg']); $topichref = URL::topic($row['id_topic'], $row['first_subject'], 0); $bhref = URL::board($row['id_board'], $row['bname'], 0, false); $fhref = empty($row['id_first_member']) ? '' : URL::user($row['id_first_member'], $row['first_poster_name']); $userids[$row['id_msg']] = $row['id_member']; $context['posts'][$row['id_msg']] = array('id' => $row['id_msg'], 'counter' => $counter++, 'sequence_number' => true, 'icon' => $row['icon'], 'icon_url' => getPostIcon($row['icon']), 'category' => array('id' => $row['id_cat'], 'name' => $row['cname'], 'href' => $scripturl . '#c' . $row['id_cat'], 'link' => '<a href="' . $scripturl . '#c' . $row['id_cat'] . '">' . $row['cname'] . '</a>'), 'board' => array('id' => $row['id_board'], 'name' => $row['bname'], 'href' => $bhref, 'link' => '<a href="' . $bhref . '">' . $row['bname'] . '</a>'), 'href' => $thref, 'link' => '<a href="' . $thref . '" rel="nofollow">' . $row['subject'] . '</a>', 'start' => $row['num_replies'], 'subject' => $row['subject'], 'time' => timeformat($row['poster_time']), 'timestamp' => forum_time(true, $row['poster_time']), 'first_poster' => array('id' => $row['id_first_member'], 'name' => $row['first_poster_name'], 'href' => $fhref, 'link' => empty($row['id_first_member']) ? $row['first_poster_name'] : '<a href="' . $fhref . '">' . $row['first_poster_name'] . '</a>', 'time' => timeformat($row['time_started'])), 'topic' => array('id' => $row['id_topic'], 'href' => $topichref, 'link' => '<a href="' . $topichref . '" rel="nofollow">' . $row['first_subject'] . '</a>'), 'permahref' => URL::parse('?msg=' . $row['id_msg']), 'permalink' => $txt['view_in_thread'], 'id_member' => $row['id_member'], 'body' => $row['body'], 'can_reply' => false, 'can_mark_notify' => false, 'can_delete' => false, 'delete_possible' => ($row['id_first_msg'] != $row['id_msg'] || $row['id_last_msg'] == $row['id_msg']) && (empty($modSettings['edit_disable_time']) || $row['poster_time'] + $modSettings['edit_disable_time'] * 60 >= time()), 'likes_count' => $row['likes_count'], 'like_status' => $row['like_status'], 'liked' => $row['liked'], 'like_updated' => $row['like_updated'], 'likers' => '', 'likelink' => ''); if ($context['can_see_like']) { Ratings::addContent($context['posts'][$row['id_msg']], $context['can_give_like'], $context['time_now']); } if ($user_info['id'] == $row['id_first_member']) { $board_ids['own'][$row['id_board']][] = $row['id_msg']; } $board_ids['any'][$row['id_board']][] = $row['id_msg']; } mysql_free_result($request); loadMemberData(array_unique($userids)); // There might be - and are - different permissions between any and own. $permissions = array('own' => array('post_reply_own' => 'can_reply', 'delete_own' => 'can_delete'), 'any' => array('post_reply_any' => 'can_reply', 'mark_any_notify' => 'can_mark_notify', 'delete_any' => 'can_delete')); // Now go through all the permissions, looking for boards they can do it on. foreach ($permissions as $type => $list) { foreach ($list as $permission => $allowed) { // They can do it on these boards... $boards = boardsAllowedTo($permission); // If 0 is the only thing in the array, they can do it everywhere! if (!empty($boards) && $boards[0] == 0) { $boards = array_keys($board_ids[$type]); } // Go through the boards, and look for posts they can do this on. foreach ($boards as $board_id) { // Hmm, they have permission, but there are no topics from that board on this page. if (!isset($board_ids[$type][$board_id])) { continue; } // Okay, looks like they can do it for these posts. foreach ($board_ids[$type][$board_id] as $counter) { if ($type == 'any' || $context['posts'][$counter]['id_member'] == $user_info['id']) { $context['posts'][$counter][$allowed] = true; } } } } } $quote_enabled = empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC'])); foreach ($context['posts'] as $counter => &$post) { loadMemberContext($post['id_member']); $post['member'] =& $memberContext[$post['id_member']]; // Some posts - the first posts - can't just be deleted. $context['posts'][$counter]['can_delete'] &= $context['posts'][$counter]['delete_possible']; // And some cannot be quoted... $context['posts'][$counter]['can_quote'] = $context['posts'][$counter]['can_reply'] && $quote_enabled; } }
function smf_db_error($db_string, $connection = null) { global $txt, $context, $sourcedir, $webmaster_email, $modSettings; global $forum_version, $db_connection, $db_last_error, $db_persist; global $db_server, $db_user, $db_passwd, $db_name, $db_show_debug, $ssi_db_user, $ssi_db_passwd; // Get the file and line numbers. list($file, $line) = smf_db_error_backtrace('', '', 'return', __FILE__, __LINE__); // Decide which connection to use $connection = $connection == null ? $db_connection : $connection; // This is the error message... $query_error = mysql_error($connection); $query_errno = mysql_errno($connection); // Error numbers: // 1016: Can't open file '....MYI' // 1030: Got error ??? from table handler. // 1034: Incorrect key file for table. // 1035: Old key file for table. // 1205: Lock wait timeout exceeded. // 1213: Deadlock found. // 2006: Server has gone away. // 2013: Lost connection to server during query. // Log the error. if ($query_errno != 1213 && $query_errno != 1205 && function_exists('log_error')) { log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n{$db_string}" : ''), 'database', $file, $line); } // Database error auto fixing ;). if (function_exists('cache_get_data') && (!isset($modSettings['autoFixDatabase']) || $modSettings['autoFixDatabase'] == '1')) { // Force caching on, just for the error checking. $old_cache = @$modSettings['cache_enable']; $modSettings['cache_enable'] = '1'; if (($temp = CacheAPI::getCache('db_last_error', 600)) !== null) { $db_last_error = max(@$db_last_error, $temp); } if (@$db_last_error < time() - 3600 * 24 * 3) { // We know there's a problem... but what? Try to auto detect. if ($query_errno == 1030 && strpos($query_error, ' 127 ') !== false) { preg_match_all('~(?:[\\n\\r]|^)[^\']+?(?:FROM|JOIN|UPDATE|TABLE) ((?:[^\\n\\r(]+?(?:, )?)*)~s', $db_string, $matches); $fix_tables = array(); foreach ($matches[1] as $tables) { $tables = array_unique(explode(',', $tables)); foreach ($tables as $table) { // Now, it's still theoretically possible this could be an injection. So backtick it! if (trim($table) != '') { $fix_tables[] = '`' . strtr(trim($table), array('`' => '')) . '`'; } } } $fix_tables = array_unique($fix_tables); } elseif ($query_errno == 1016) { if (preg_match('~\'([^\\.\']+)~', $query_error, $match) != 0) { $fix_tables = array('`' . $match[1] . '`'); } } elseif ($query_errno == 1034 || $query_errno == 1035) { preg_match('~\'([^\']+?)\'~', $query_error, $match); $fix_tables = array('`' . $match[1] . '`'); } } // Check for errors like 145... only fix it once every three days, and send an email. (can't use empty because it might not be set yet...) if (!empty($fix_tables)) { // Subs-Admin.php for updateSettingsFile(), Subs-Post.php for sendmail(). require_once $sourcedir . '/lib/Subs-Admin.php'; require_once $sourcedir . '/lib/Subs-Post.php'; // Make a note of the REPAIR... CacheAPI::putCache('db_last_error', time(), 600); if (($temp = CacheAPI::getCache('db_last_error', 600)) === null) { updateSettingsFile(array('db_last_error' => time())); } // Attempt to find and repair the broken table. foreach ($fix_tables as $table) { smf_db_query("\n\t\t\t\t\tREPAIR TABLE {$table}", false, false); } // And send off an email! sendmail($webmaster_email, $txt['database_error'], $txt['tried_to_repair']); $modSettings['cache_enable'] = $old_cache; // Try the query again...? $ret = smf_db_query($db_string, false, false); if ($ret !== false) { return $ret; } } else { $modSettings['cache_enable'] = $old_cache; } // Check for the "lost connection" or "deadlock found" errors - and try it just one more time. if (in_array($query_errno, array(1205, 1213, 2006, 2013))) { if (in_array($query_errno, array(2006, 2013)) && $db_connection == $connection) { // Are we in SSI mode? If so try that username and password first if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd)) { if (empty($db_persist)) { $db_connection = @mysql_connect($db_server, $ssi_db_user, $ssi_db_passwd); } else { $db_connection = @mysql_pconnect($db_server, $ssi_db_user, $ssi_db_passwd); } } // Fall back to the regular username and password if need be if (!$db_connection) { if (empty($db_persist)) { $db_connection = @mysql_connect($db_server, $db_user, $db_passwd); } else { $db_connection = @mysql_pconnect($db_server, $db_user, $db_passwd); } } if (!$db_connection || !@mysql_select_db($db_name, $db_connection)) { $db_connection = false; } } if ($db_connection) { // Try a deadlock more than once more. for ($n = 0; $n < 4; $n++) { $ret = smf_db_query($db_string, false, false); $new_errno = mysql_errno($db_connection); if ($ret !== false || in_array($new_errno, array(1205, 1213))) { break; } } // If it failed again, shucks to be you... we're not trying it over and over. if ($ret !== false) { return $ret; } } } elseif ($query_errno == 1030 && (strpos($query_error, ' -1 ') !== false || strpos($query_error, ' 28 ') !== false || strpos($query_error, ' 12 ') !== false)) { if (!isset($txt)) { $query_error .= ' - check database storage space.'; } else { if (!isset($txt['mysql_error_space'])) { loadLanguage('Errors'); } $query_error .= !isset($txt['mysql_error_space']) ? ' - check database storage space.' : $txt['mysql_error_space']; } } } // Nothing's defined yet... just die with it. if (empty($context) || empty($txt)) { die($query_error); } // Show an error message, if possible. $context['error_title'] = $txt['database_error']; if (allowedTo('admin_forum')) { $context['error_message'] = nl2br($query_error) . '<br />' . $txt['file'] . ': ' . $file . '<br />' . $txt['line'] . ': ' . $line; } else { $context['error_message'] = $txt['try_again']; } // A database error is often the sign of a database in need of upgrade. Check forum versions, and if not identical suggest an upgrade... (not for Demo/CVS versions!) if (allowedTo('admin_forum') && !empty($forum_version) && $forum_version != 'SMF ' . @$modSettings['smfVersion'] && strpos($forum_version, 'Demo') === false && strpos($forum_version, 'CVS') === false) { $context['error_message'] .= '<br /><br />' . sprintf($txt['database_error_versions'], $forum_version, $modSettings['smfVersion']); } if (allowedTo('admin_forum') && isset($db_show_debug) && $db_show_debug === true) { $context['error_message'] .= '<br /><br />' . nl2br($db_string); } // It's already been logged... don't log it again. fatal_error($context['error_message'], false); }
function DisplayStats() { global $txt, $scripturl, $modSettings, $user_info, $context, $smcFunc; isAllowedTo('view_stats'); if (!empty($_REQUEST['expand'])) { $context['robot_no_index'] = true; $month = (int) substr($_REQUEST['expand'], 4); $year = (int) substr($_REQUEST['expand'], 0, 4); if ($year > 1900 && $year < 2200 && $month >= 1 && $month <= 12) { $_SESSION['expanded_stats'][$year][] = $month; } } elseif (!empty($_REQUEST['collapse'])) { $context['robot_no_index'] = true; $month = (int) substr($_REQUEST['collapse'], 4); $year = (int) substr($_REQUEST['collapse'], 0, 4); if (!empty($_SESSION['expanded_stats'][$year])) { $_SESSION['expanded_stats'][$year] = array_diff($_SESSION['expanded_stats'][$year], array($month)); } } // Handle the XMLHttpRequest. if (isset($_REQUEST['xml'])) { // Collapsing stats only needs adjustments of the session variables. if (!empty($_REQUEST['collapse'])) { obExit(false); } $context['sub_template'] = 'stats'; getDailyStats('YEAR(date) = {int:year} AND MONTH(date) = {int:month}', array('year' => $year, 'month' => $month)); $context['yearly'][$year]['months'][$month]['date'] = array('month' => sprintf('%02d', $month), 'year' => $year); return; } loadLanguage('Stats'); Eos_Smarty::loadTemplate('forumstats'); // Build the link tree...... $context['linktree'][] = array('url' => $scripturl . '?action=stats', 'name' => $txt['stats_center']); $context['page_title'] = $context['forum_name'] . ' - ' . $txt['stats_center']; $context['show_member_list'] = allowedTo('view_mlist'); // Get averages... $result = smf_db_query(' SELECT SUM(posts) AS posts, SUM(topics) AS topics, SUM(registers) AS registers, SUM(most_on) AS most_on, MIN(date) AS date, SUM(hits) AS hits FROM {db_prefix}log_activity', array()); $row = mysql_fetch_assoc($result); mysql_free_result($result); // This would be the amount of time the forum has been up... in days... $total_days_up = ceil((time() - strtotime($row['date'])) / (60 * 60 * 24)); $context['average_posts'] = comma_format(round($row['posts'] / $total_days_up, 2)); $context['average_topics'] = comma_format(round($row['topics'] / $total_days_up, 2)); $context['average_members'] = comma_format(round($row['registers'] / $total_days_up, 2)); $context['average_online'] = comma_format(round($row['most_on'] / $total_days_up, 2)); $context['average_hits'] = comma_format(round($row['hits'] / $total_days_up, 2)); $context['num_hits'] = comma_format($row['hits'], 0); // How many users are online now. $result = smf_db_query(' SELECT COUNT(*) FROM {db_prefix}log_online', array()); list($context['users_online']) = mysql_fetch_row($result); mysql_free_result($result); // Statistics such as number of boards, categories, etc. $result = smf_db_query(' SELECT COUNT(*) FROM {db_prefix}boards AS b WHERE b.redirect = {string:blank_redirect}', array('blank_redirect' => '')); list($context['num_boards']) = mysql_fetch_row($result); mysql_free_result($result); $result = smf_db_query(' SELECT COUNT(*) FROM {db_prefix}categories AS c', array()); list($context['num_categories']) = mysql_fetch_row($result); mysql_free_result($result); // Format the numbers nicely. $context['users_online'] = comma_format($context['users_online']); $context['num_boards'] = comma_format($context['num_boards']); $context['num_categories'] = comma_format($context['num_categories']); $context['num_members'] = comma_format($modSettings['totalMembers']); $context['num_posts'] = comma_format($modSettings['totalMessages']); $context['num_topics'] = comma_format($modSettings['totalTopics']); $context['most_members_online'] = array('number' => comma_format($modSettings['mostOnline']), 'date' => timeformat($modSettings['mostDate'])); $context['latest_member'] =& $context['common_stats']['latest_member']; // Male vs. female ratio - let's calculate this only every four minutes. if (($context['gender'] = CacheAPI::getCache('stats_gender', 240)) == null) { $result = smf_db_query(' SELECT COUNT(*) AS total_members, gender FROM {db_prefix}members GROUP BY gender', array()); $context['gender'] = array(); while ($row = mysql_fetch_assoc($result)) { // Assuming we're telling... male or female? if (!empty($row['gender'])) { $context['gender'][$row['gender'] == 2 ? 'females' : 'males'] = $row['total_members']; } } mysql_free_result($result); // Set these two zero if the didn't get set at all. if (empty($context['gender']['males'])) { $context['gender']['males'] = 0; } if (empty($context['gender']['females'])) { $context['gender']['females'] = 0; } // Try and come up with some "sensible" default states in case of a non-mixed board. if ($context['gender']['males'] == $context['gender']['females']) { $context['gender']['ratio'] = '1:1'; } elseif ($context['gender']['males'] == 0) { $context['gender']['ratio'] = '0:1'; } elseif ($context['gender']['females'] == 0) { $context['gender']['ratio'] = '1:0'; } elseif ($context['gender']['males'] > $context['gender']['females']) { $context['gender']['ratio'] = round($context['gender']['males'] / $context['gender']['females'], 1) . ':1'; } elseif ($context['gender']['females'] > $context['gender']['males']) { $context['gender']['ratio'] = '1:' . round($context['gender']['females'] / $context['gender']['males'], 1); } CacheAPI::putCache('stats_gender', $context['gender'], 240); } $date = strftime('%Y-%m-%d', forum_time(false)); // Members online so far today. $result = smf_db_query(' SELECT most_on FROM {db_prefix}log_activity WHERE date = {date:today_date} LIMIT 1', array('today_date' => $date)); list($context['online_today']) = mysql_fetch_row($result); mysql_free_result($result); $context['online_today'] = comma_format((int) $context['online_today']); // Poster top 10. $members_result = smf_db_query(' SELECT id_member, real_name, posts FROM {db_prefix}members WHERE posts > {int:no_posts} ORDER BY posts DESC LIMIT 10', array('no_posts' => 0)); $context['top_posters'] = array(); $max_num_posts = 1; while ($row_members = mysql_fetch_assoc($members_result)) { $context['top_posters'][] = array('name' => $row_members['real_name'], 'id' => $row_members['id_member'], 'num_posts' => $row_members['posts'], 'href' => $scripturl . '?action=profile;u=' . $row_members['id_member'], 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row_members['id_member'] . '">' . $row_members['real_name'] . '</a>'); if ($max_num_posts < $row_members['posts']) { $max_num_posts = $row_members['posts']; } } mysql_free_result($members_result); foreach ($context['top_posters'] as $i => $poster) { $context['top_posters'][$i]['post_percent'] = round($poster['num_posts'] * 100 / $max_num_posts); $context['top_posters'][$i]['num_posts'] = comma_format($context['top_posters'][$i]['num_posts']); } // Board top 10. $boards_result = smf_db_query(' SELECT id_board, name, num_posts FROM {db_prefix}boards AS b WHERE {query_see_board}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' AND b.id_board != {int:recycle_board}' : '') . ' AND b.redirect = {string:blank_redirect} ORDER BY num_posts DESC LIMIT 10', array('recycle_board' => $modSettings['recycle_board'], 'blank_redirect' => '')); $context['top_boards'] = array(); $max_num_posts = 1; while ($row_board = mysql_fetch_assoc($boards_result)) { $context['top_boards'][] = array('id' => $row_board['id_board'], 'name' => $row_board['name'], 'num_posts' => $row_board['num_posts'], 'href' => $scripturl . '?board=' . $row_board['id_board'] . '.0', 'link' => '<a href="' . $scripturl . '?board=' . $row_board['id_board'] . '.0">' . $row_board['name'] . '</a>'); if ($max_num_posts < $row_board['num_posts']) { $max_num_posts = $row_board['num_posts']; } } mysql_free_result($boards_result); foreach ($context['top_boards'] as $i => $board) { $context['top_boards'][$i]['post_percent'] = round($board['num_posts'] * 100 / $max_num_posts); $context['top_boards'][$i]['num_posts'] = comma_format($context['top_boards'][$i]['num_posts']); } // Are you on a larger forum? If so, let's try to limit the number of topics we search through. if ($modSettings['totalMessages'] > 100000) { $request = smf_db_query(' SELECT id_topic FROM {db_prefix}topics WHERE num_replies != {int:no_replies}' . ($modSettings['postmod_active'] ? ' AND approved = {int:is_approved}' : '') . ' ORDER BY num_replies DESC LIMIT 100', array('no_replies' => 0, 'is_approved' => 1)); $topic_ids = array(); while ($row = mysql_fetch_assoc($request)) { $topic_ids[] = $row['id_topic']; } mysql_free_result($request); } else { $topic_ids = array(); } // Topic replies top 10. $topic_reply_result = smf_db_query(' SELECT m.subject, t.num_replies, t.id_board, t.id_topic, b.name FROM {db_prefix}topics AS t INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' AND b.id_board != {int:recycle_board}' : '') . ') WHERE {query_see_board}' . (!empty($topic_ids) ? ' AND t.id_topic IN ({array_int:topic_list})' : ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '')) . ' ORDER BY t.num_replies DESC LIMIT 10', array('topic_list' => $topic_ids, 'recycle_board' => $modSettings['recycle_board'], 'is_approved' => 1)); $context['top_topics_replies'] = array(); $max_num_replies = 1; while ($row_topic_reply = mysql_fetch_assoc($topic_reply_result)) { censorText($row_topic_reply['subject']); $context['top_topics_replies'][] = array('id' => $row_topic_reply['id_topic'], 'board' => array('id' => $row_topic_reply['id_board'], 'name' => $row_topic_reply['name'], 'href' => $scripturl . '?board=' . $row_topic_reply['id_board'] . '.0', 'link' => '<a href="' . $scripturl . '?board=' . $row_topic_reply['id_board'] . '.0">' . $row_topic_reply['name'] . '</a>'), 'subject' => $row_topic_reply['subject'], 'num_replies' => $row_topic_reply['num_replies'], 'href' => $scripturl . '?topic=' . $row_topic_reply['id_topic'] . '.0', 'link' => '<a href="' . $scripturl . '?topic=' . $row_topic_reply['id_topic'] . '.0">' . $row_topic_reply['subject'] . '</a>'); if ($max_num_replies < $row_topic_reply['num_replies']) { $max_num_replies = $row_topic_reply['num_replies']; } } mysql_free_result($topic_reply_result); foreach ($context['top_topics_replies'] as $i => $topic) { $context['top_topics_replies'][$i]['post_percent'] = round($topic['num_replies'] * 100 / $max_num_replies); $context['top_topics_replies'][$i]['num_replies'] = comma_format($context['top_topics_replies'][$i]['num_replies']); } // Large forums may need a bit more prodding... if ($modSettings['totalMessages'] > 100000) { $request = smf_db_query(' SELECT id_topic FROM {db_prefix}topics WHERE num_views != {int:no_views} ORDER BY num_views DESC LIMIT 100', array('no_views' => 0)); $topic_ids = array(); while ($row = mysql_fetch_assoc($request)) { $topic_ids[] = $row['id_topic']; } mysql_free_result($request); } else { $topic_ids = array(); } // Topic views top 10. $topic_view_result = smf_db_query(' SELECT m.subject, t.num_views, t.id_board, t.id_topic, b.name FROM {db_prefix}topics AS t INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' AND b.id_board != {int:recycle_board}' : '') . ') WHERE {query_see_board}' . (!empty($topic_ids) ? ' AND t.id_topic IN ({array_int:topic_list})' : ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '')) . ' ORDER BY t.num_views DESC LIMIT 10', array('topic_list' => $topic_ids, 'recycle_board' => $modSettings['recycle_board'], 'is_approved' => 1)); $context['top_topics_views'] = array(); $max_num_views = 1; while ($row_topic_views = mysql_fetch_assoc($topic_view_result)) { censorText($row_topic_views['subject']); $context['top_topics_views'][] = array('id' => $row_topic_views['id_topic'], 'board' => array('id' => $row_topic_views['id_board'], 'name' => $row_topic_views['name'], 'href' => $scripturl . '?board=' . $row_topic_views['id_board'] . '.0', 'link' => '<a href="' . $scripturl . '?board=' . $row_topic_views['id_board'] . '.0">' . $row_topic_views['name'] . '</a>'), 'subject' => $row_topic_views['subject'], 'num_views' => $row_topic_views['num_views'], 'href' => $scripturl . '?topic=' . $row_topic_views['id_topic'] . '.0', 'link' => '<a href="' . $scripturl . '?topic=' . $row_topic_views['id_topic'] . '.0">' . $row_topic_views['subject'] . '</a>'); if ($max_num_views < $row_topic_views['num_views']) { $max_num_views = $row_topic_views['num_views']; } } mysql_free_result($topic_view_result); foreach ($context['top_topics_views'] as $i => $topic) { $context['top_topics_views'][$i]['post_percent'] = round($topic['num_views'] * 100 / $max_num_views); $context['top_topics_views'][$i]['num_views'] = comma_format($context['top_topics_views'][$i]['num_views']); } // Try to cache this when possible, because it's a little unavoidably slow. if (($members = CacheAPI::getCache('stats_top_starters', 360)) == null) { $request = smf_db_query(' SELECT id_member_started, COUNT(*) AS hits FROM {db_prefix}topics' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? ' WHERE id_board != {int:recycle_board}' : '') . ' GROUP BY id_member_started ORDER BY hits DESC LIMIT 20', array('recycle_board' => $modSettings['recycle_board'])); $members = array(); while ($row = mysql_fetch_assoc($request)) { $members[$row['id_member_started']] = $row['hits']; } mysql_free_result($request); CacheAPI::putCache('stats_top_starters', $members, 360); } if (empty($members)) { $members = array(0 => 0); } // Topic poster top 10. $members_result = smf_db_query(' SELECT id_member, real_name FROM {db_prefix}members WHERE id_member IN ({array_int:member_list}) ORDER BY FIND_IN_SET(id_member, {string:top_topic_posters}) LIMIT 10', array('member_list' => array_keys($members), 'top_topic_posters' => implode(',', array_keys($members)))); $context['top_starters'] = array(); $max_num_topics = 1; while ($row_members = mysql_fetch_assoc($members_result)) { $context['top_starters'][] = array('name' => $row_members['real_name'], 'id' => $row_members['id_member'], 'num_topics' => $members[$row_members['id_member']], 'href' => $scripturl . '?action=profile;u=' . $row_members['id_member'], 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row_members['id_member'] . '">' . $row_members['real_name'] . '</a>'); if ($max_num_topics < $members[$row_members['id_member']]) { $max_num_topics = $members[$row_members['id_member']]; } } mysql_free_result($members_result); foreach ($context['top_starters'] as $i => $topic) { $context['top_starters'][$i]['post_percent'] = round($topic['num_topics'] * 100 / $max_num_topics); $context['top_starters'][$i]['num_topics'] = comma_format($context['top_starters'][$i]['num_topics']); } // Time online top 10. $temp = CacheAPI::getCache('stats_total_time_members', 600); $members_result = smf_db_query(' SELECT id_member, real_name, total_time_logged_in FROM {db_prefix}members' . (!empty($temp) ? ' WHERE id_member IN ({array_int:member_list_cached})' : '') . ' ORDER BY total_time_logged_in DESC LIMIT 20', array('member_list_cached' => $temp)); $context['top_time_online'] = array(); $temp2 = array(); $max_time_online = 1; while ($row_members = mysql_fetch_assoc($members_result)) { $temp2[] = (int) $row_members['id_member']; if (count($context['top_time_online']) >= 10) { continue; } // Figure out the days, hours and minutes. $timeDays = floor($row_members['total_time_logged_in'] / 86400); $timeHours = floor($row_members['total_time_logged_in'] % 86400 / 3600); // Figure out which things to show... (days, hours, minutes, etc.) $timelogged = ''; if ($timeDays > 0) { $timelogged .= $timeDays . $txt['totalTimeLogged5']; } if ($timeHours > 0) { $timelogged .= $timeHours . $txt['totalTimeLogged6']; } $timelogged .= floor($row_members['total_time_logged_in'] % 3600 / 60) . $txt['totalTimeLogged7']; $context['top_time_online'][] = array('id' => $row_members['id_member'], 'name' => $row_members['real_name'], 'time_online' => $timelogged, 'seconds_online' => $row_members['total_time_logged_in'], 'href' => $scripturl . '?action=profile;u=' . $row_members['id_member'], 'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row_members['id_member'] . '">' . $row_members['real_name'] . '</a>'); if ($max_time_online < $row_members['total_time_logged_in']) { $max_time_online = $row_members['total_time_logged_in']; } } mysql_free_result($members_result); foreach ($context['top_time_online'] as $i => $member) { $context['top_time_online'][$i]['time_percent'] = round($member['seconds_online'] * 100 / $max_time_online); } // Cache the ones we found for a bit, just so we don't have to look again. if ($temp !== $temp2) { CacheAPI::putCache('stats_total_time_members', $temp2, 480); } // Activity by month. $months_result = smf_db_query(' SELECT YEAR(date) AS stats_year, MONTH(date) AS stats_month, SUM(hits) AS hits, SUM(registers) AS registers, SUM(topics) AS topics, SUM(posts) AS posts, MAX(most_on) AS most_on, COUNT(*) AS num_days FROM {db_prefix}log_activity GROUP BY stats_year, stats_month', array()); $context['yearly'] = array(); while ($row_months = mysql_fetch_assoc($months_result)) { $ID_MONTH = $row_months['stats_year'] . sprintf('%02d', $row_months['stats_month']); $expanded = !empty($_SESSION['expanded_stats'][$row_months['stats_year']]) && in_array($row_months['stats_month'], $_SESSION['expanded_stats'][$row_months['stats_year']]); if (!isset($context['yearly'][$row_months['stats_year']])) { $context['yearly'][$row_months['stats_year']] = array('year' => $row_months['stats_year'], 'new_topics' => 0, 'new_posts' => 0, 'new_members' => 0, 'most_members_online' => 0, 'hits' => 0, 'num_months' => 0, 'months' => array(), 'expanded' => false, 'current_year' => $row_months['stats_year'] == date('Y')); } $context['yearly'][$row_months['stats_year']]['months'][(int) $row_months['stats_month']] = array('id' => $ID_MONTH, 'date' => array('month' => sprintf('%02d', $row_months['stats_month']), 'year' => $row_months['stats_year']), 'href' => $scripturl . '?action=stats;' . ($expanded ? 'collapse' : 'expand') . '=' . $ID_MONTH . '#m' . $ID_MONTH, 'link' => '<a href="' . $scripturl . '?action=stats;' . ($expanded ? 'collapse' : 'expand') . '=' . $ID_MONTH . '#m' . $ID_MONTH . '">' . $txt['months'][(int) $row_months['stats_month']] . ' ' . $row_months['stats_year'] . '</a>', 'month' => $txt['months'][(int) $row_months['stats_month']], 'year' => $row_months['stats_year'], 'new_topics' => comma_format($row_months['topics']), 'new_posts' => comma_format($row_months['posts']), 'new_members' => comma_format($row_months['registers']), 'most_members_online' => comma_format($row_months['most_on']), 'hits' => comma_format($row_months['hits']), 'num_days' => $row_months['num_days'], 'days' => array(), 'expanded' => $expanded); $context['yearly'][$row_months['stats_year']]['new_topics'] += $row_months['topics']; $context['yearly'][$row_months['stats_year']]['new_posts'] += $row_months['posts']; $context['yearly'][$row_months['stats_year']]['new_members'] += $row_months['registers']; $context['yearly'][$row_months['stats_year']]['hits'] += $row_months['hits']; $context['yearly'][$row_months['stats_year']]['num_months']++; $context['yearly'][$row_months['stats_year']]['expanded'] |= $expanded; $context['yearly'][$row_months['stats_year']]['most_members_online'] = max($context['yearly'][$row_months['stats_year']]['most_members_online'], $row_months['most_on']); } krsort($context['yearly']); $context['collapsed_years'] = array(); foreach ($context['yearly'] as $year => $data) { // This gets rid of the filesort on the query ;). krsort($context['yearly'][$year]['months']); $context['yearly'][$year]['new_topics'] = comma_format($data['new_topics']); $context['yearly'][$year]['new_posts'] = comma_format($data['new_posts']); $context['yearly'][$year]['new_members'] = comma_format($data['new_members']); $context['yearly'][$year]['most_members_online'] = comma_format($data['most_members_online']); $context['yearly'][$year]['hits'] = comma_format($data['hits']); // Keep a list of collapsed years. if (!$data['expanded'] && !$data['current_year']) { $context['collapsed_years'][] = $year; } } if (empty($_SESSION['expanded_stats'])) { return; } $condition_text = array(); $condition_params = array(); foreach ($_SESSION['expanded_stats'] as $year => $months) { if (!empty($months)) { $condition_text[] = 'YEAR(date) = {int:year_' . $year . '} AND MONTH(date) IN ({array_int:months_' . $year . '})'; $condition_params['year_' . $year] = $year; $condition_params['months_' . $year] = $months; } } // No daily stats to even look at? if (empty($condition_text)) { return; } getDailyStats(implode(' OR ', $condition_text), $condition_params); }
function show_db_error($loadavg = false) { global $sourcedir, $mbname, $maintenance, $mtitle, $mmessage, $modSettings; global $db_connection, $webmaster_email, $db_last_error, $db_error_send, $smcFunc; // Don't cache this page! header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-cache'); // Send the right error codes. header('HTTP/1.1 503 Service Temporarily Unavailable'); header('Status: 503 Service Temporarily Unavailable'); header('Retry-After: 3600'); if ($loadavg == false) { // For our purposes, we're gonna want this on if at all possible. $modSettings['cache_enable'] = '1'; if (($temp = CacheAPI::getCache('db_last_error', 600)) !== null) { $db_last_error = max($db_last_error, $temp); } if ($db_last_error < time() - 3600 * 24 * 3 && empty($maintenance) && !empty($db_error_send)) { require_once $sourcedir . '/lib/Subs-Admin.php'; // Avoid writing to the Settings.php file if at all possible; use shared memory instead. CacheAPI::putCache('db_last_error', time(), 600); if (($temp = CacheAPI::getCache('db_last_error', 600)) == null) { updateLastDatabaseError(); } // Language files aren't loaded yet :(. $db_error = @mysql_error($db_connection); @mail($webmaster_email, $mbname . ': SMF Database Error!', 'There has been a problem with the database!' . ($db_error == '' ? '' : "\n" . 'MySQL' . ' reported:' . "\n" . $db_error) . "\n\n" . 'This is a notice email to let you know that SMF could not connect to the database, contact your host if this continues.'); } } if (!empty($maintenance)) { echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta name="robots" content="noindex" /> <title>', $mtitle, '</title> </head> <body> <h3>', $mtitle, '</h3> ', $mmessage, ' </body> </html>'; } elseif ($loadavg) { echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta name="robots" content="noindex" /> <title>Temporarily Unavailable</title> </head> <body> <h3>Temporarily Unavailable</h3> Due to high stress on the server the forum is temporarily unavailable. Please try again later. </body> </html>'; } else { echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta name="robots" content="noindex" /> <title>Connection Problems</title> </head> <body> <h3>Connection Problems</h3> Sorry, SMF was unable to connect to the database. This may be caused by the server being busy. Please try again later. </body> </html>'; } die; }
/** * Loads all board names from the forum into a variable and cache (if possible) * This helps reduce the number of queries needed for SimpleSEF to run * * @global array $smcFunc * @global string $language * @param boolean $force Forces a reload of board names */ private static function loadBoardNames($force = FALSE) { global $language; if ($force || (self::$boardNames = CacheAPI::getCache('simplesef_board_list', 3600)) == NULL) { loadLanguage('index', $language, false); $request = smf_db_query(' SELECT id_board, name FROM {db_prefix}boards', array()); $boards = array(); while ($row = mysql_fetch_assoc($request)) { // A bit extra overhead to account for duplicate board names $temp_name = self::encode($row['name']); $i = 0; while (!empty($boards[$temp_name . (!empty($i) ? $i + 1 : '')])) { $i++; } //$boards[$temp_name . (!empty($i) ? $i + 1 : '')] = $row['id_board']; $boards[$temp_name . '.' . trim($row['id_board'])] = $row['id_board']; } mysql_free_result($request); self::$boardNames = array_flip($boards); // Add one to the query cound and put the data into the cache self::$queryCount++; CacheAPI::putCache('simplesef_board_list', self::$boardNames, 3600); //self::log('Cache hit failed, reloading board names'); } }
function create_control_verification(&$verificationOptions, $do_test = false) { global $txt, $modSettings, $options, $smcFunc; global $context, $settings, $user_info, $sourcedir, $scripturl; // First verification means we need to set up some bits... if (empty($context['controls']['verification'])) { // The template //if(!isset($verificationOptions['skip_template'])) // loadTemplate('GenericControls'); // Some javascript ma'am? if (!empty($verificationOptions['override_visual']) || !empty($modSettings['visual_verification_type']) && !isset($verificationOptions['override_visual'])) { $context['html_headers'] .= ' <script type="text/javascript" src="' . $settings['default_theme_url'] . '/scripts/captcha.js"></script>'; } $context['use_graphic_library'] = in_array('gd', get_loaded_extensions()); // Skip I, J, L, O, Q, S and Z. $context['standard_captcha_range'] = array_merge(range('A', 'H'), array('K', 'M', 'N', 'P', 'R'), range('T', 'Y')); } // Always have an ID. assert(isset($verificationOptions['id'])); $isNew = !isset($context['controls']['verification'][$verificationOptions['id']]); // Log this into our collection. if ($isNew) { $context['controls']['verification'][$verificationOptions['id']] = array('id' => $verificationOptions['id'], 'show_visual' => !empty($verificationOptions['override_visual']) || !empty($modSettings['visual_verification_type']) && !isset($verificationOptions['override_visual']), 'number_questions' => isset($verificationOptions['override_qs']) ? $verificationOptions['override_qs'] : (!empty($modSettings['qa_verification_number']) ? $modSettings['qa_verification_number'] : 0), 'max_errors' => isset($verificationOptions['max_errors']) ? $verificationOptions['max_errors'] : 3, 'image_href' => $scripturl . '?action=verificationcode;vid=' . $verificationOptions['id'] . ';rand=' . md5(mt_rand()), 'text_value' => '', 'questions' => array()); } $thisVerification =& $context['controls']['verification'][$verificationOptions['id']]; // Add javascript for the object. if ($context['controls']['verification'][$verificationOptions['id']]['show_visual']) { $context['inline_footer_script'] .= ' var verification' . $verificationOptions['id'] . 'Handle = new smfCaptcha("' . $thisVerification['image_href'] . '", "' . $verificationOptions['id'] . '", ' . ($context['use_graphic_library'] ? 1 : 0) . ');'; } // Is there actually going to be anything? if (empty($thisVerification['show_visual']) && empty($thisVerification['number_questions'])) { return false; } elseif (!$isNew && !$do_test) { return true; } // If we want questions do we have a cache of all the IDs? if (!empty($thisVerification['number_questions']) && empty($modSettings['question_id_cache'])) { if (($modSettings['question_id_cache'] = CacheAPI::getCache('verificationQuestionIds', 300)) == null) { $request = smf_db_query(' SELECT id_comment FROM {db_prefix}log_comments WHERE comment_type = {string:ver_test}', array('ver_test' => 'ver_test')); $modSettings['question_id_cache'] = array(); while ($row = mysql_fetch_assoc($request)) { $modSettings['question_id_cache'][] = $row['id_comment']; } mysql_free_result($request); if (!empty($modSettings['cache_enable'])) { CacheAPI::putCache('verificationQuestionIds', $modSettings['question_id_cache'], 300); } } } if (!isset($_SESSION[$verificationOptions['id'] . '_vv'])) { $_SESSION[$verificationOptions['id'] . '_vv'] = array(); } // Do we need to refresh the verification? if (!$do_test && (!empty($_SESSION[$verificationOptions['id'] . '_vv']['did_pass']) || empty($_SESSION[$verificationOptions['id'] . '_vv']['count']) || $_SESSION[$verificationOptions['id'] . '_vv']['count'] > 3) && empty($verificationOptions['dont_refresh'])) { $force_refresh = true; } else { $force_refresh = false; } // This can also force a fresh, although unlikely. if ($thisVerification['show_visual'] && empty($_SESSION[$verificationOptions['id'] . '_vv']['code']) || $thisVerification['number_questions'] && empty($_SESSION[$verificationOptions['id'] . '_vv']['q'])) { $force_refresh = true; } $verification_errors = array(); // Start with any testing. if ($do_test) { // This cannot happen! if (!isset($_SESSION[$verificationOptions['id'] . '_vv']['count'])) { fatal_lang_error('no_access', false); } // ... nor this! if ($thisVerification['number_questions'] && (!isset($_SESSION[$verificationOptions['id'] . '_vv']['q']) || !isset($_REQUEST[$verificationOptions['id'] . '_vv']['q']))) { fatal_lang_error('no_access', false); } if ($thisVerification['show_visual'] && (empty($_REQUEST[$verificationOptions['id'] . '_vv']['code']) || empty($_SESSION[$verificationOptions['id'] . '_vv']['code']) || strtoupper($_REQUEST[$verificationOptions['id'] . '_vv']['code']) !== $_SESSION[$verificationOptions['id'] . '_vv']['code'])) { $verification_errors[] = 'wrong_verification_code'; } if ($thisVerification['number_questions']) { // Get the answers and see if they are all right! $request = smf_db_query(' SELECT id_comment, recipient_name AS answer FROM {db_prefix}log_comments WHERE comment_type = {string:ver_test} AND id_comment IN ({array_int:comment_ids})', array('ver_test' => 'ver_test', 'comment_ids' => $_SESSION[$verificationOptions['id'] . '_vv']['q'])); $incorrectQuestions = array(); while ($row = mysql_fetch_assoc($request)) { if (empty($_REQUEST[$verificationOptions['id'] . '_vv']['q'][$row['id_comment']]) || trim(commonAPI::htmlspecialchars(strtolower($_REQUEST[$verificationOptions['id'] . '_vv']['q'][$row['id_comment']]))) != strtolower($row['answer'])) { $incorrectQuestions[] = $row['id_comment']; } } mysql_free_result($request); if (!empty($incorrectQuestions)) { $verification_errors[] = 'wrong_verification_answer'; } } } // Any errors means we refresh potentially. if (!empty($verification_errors)) { if (empty($_SESSION[$verificationOptions['id'] . '_vv']['errors'])) { $_SESSION[$verificationOptions['id'] . '_vv']['errors'] = 0; } elseif ($_SESSION[$verificationOptions['id'] . '_vv']['errors'] > $thisVerification['max_errors']) { $force_refresh = true; } // Keep a track of these. $_SESSION[$verificationOptions['id'] . '_vv']['errors']++; } // Are we refreshing then? if ($force_refresh) { // Assume nothing went before. $_SESSION[$verificationOptions['id'] . '_vv']['count'] = 0; $_SESSION[$verificationOptions['id'] . '_vv']['errors'] = 0; $_SESSION[$verificationOptions['id'] . '_vv']['did_pass'] = false; $_SESSION[$verificationOptions['id'] . '_vv']['q'] = array(); $_SESSION[$verificationOptions['id'] . '_vv']['code'] = ''; // Generating a new image. if ($thisVerification['show_visual']) { // Are we overriding the range? $character_range = !empty($verificationOptions['override_range']) ? $verificationOptions['override_range'] : $context['standard_captcha_range']; for ($i = 0; $i < 6; $i++) { $_SESSION[$verificationOptions['id'] . '_vv']['code'] .= $character_range[array_rand($character_range)]; } } // Getting some new questions? if ($thisVerification['number_questions']) { // Pick some random IDs $questionIDs = array(); if ($thisVerification['number_questions'] == 1) { $questionIDs[] = $modSettings['question_id_cache'][array_rand($modSettings['question_id_cache'], $thisVerification['number_questions'])]; } else { foreach (array_rand($modSettings['question_id_cache'], $thisVerification['number_questions']) as $index) { $questionIDs[] = $modSettings['question_id_cache'][$index]; } } } } else { // Same questions as before. $questionIDs = !empty($_SESSION[$verificationOptions['id'] . '_vv']['q']) ? $_SESSION[$verificationOptions['id'] . '_vv']['q'] : array(); $thisVerification['text_value'] = !empty($_REQUEST[$verificationOptions['id'] . '_vv']['code']) ? commonAPI::htmlspecialchars($_REQUEST[$verificationOptions['id'] . '_vv']['code']) : ''; } // Have we got some questions to load? if (!empty($questionIDs)) { $request = smf_db_query(' SELECT id_comment, body AS question FROM {db_prefix}log_comments WHERE comment_type = {string:ver_test} AND id_comment IN ({array_int:comment_ids})', array('ver_test' => 'ver_test', 'comment_ids' => $questionIDs)); $_SESSION[$verificationOptions['id'] . '_vv']['q'] = array(); while ($row = mysql_fetch_assoc($request)) { $thisVerification['questions'][] = array('id' => $row['id_comment'], 'q' => parse_bbc($row['question']), 'is_error' => !empty($incorrectQuestions) && in_array($row['id_comment'], $incorrectQuestions), 'a' => isset($_REQUEST[$verificationOptions['id'] . '_vv'], $_REQUEST[$verificationOptions['id'] . '_vv']['q'], $_REQUEST[$verificationOptions['id'] . '_vv']['q'][$row['id_comment']]) ? commonAPI::htmlspecialchars($_REQUEST[$verificationOptions['id'] . '_vv']['q'][$row['id_comment']]) : ''); $_SESSION[$verificationOptions['id'] . '_vv']['q'][] = $row['id_comment']; } mysql_free_result($request); } $_SESSION[$verificationOptions['id'] . '_vv']['count'] = empty($_SESSION[$verificationOptions['id'] . '_vv']['count']) ? 1 : $_SESSION[$verificationOptions['id'] . '_vv']['count'] + 1; // Return errors if we have them. if (!empty($verification_errors)) { return $verification_errors; } elseif ($do_test) { $_SESSION[$verificationOptions['id'] . '_vv']['did_pass'] = true; } // Say that everything went well chaps. return true; }
function getMembersOnlineStats($membersOnlineOptions) { global $smcFunc, $context, $scripturl, $user_info, $modSettings, $txt, $memberContext; // The list can be sorted in several ways. $allowed_sort_options = array('log_time', 'real_name', 'show_online', 'online_color', 'group_name'); // Default the sorting method to 'most recent online members first'. if (!isset($membersOnlineOptions['sort'])) { $membersOnlineOptions['sort'] = 'log_time'; $membersOnlineOptions['reverse_sort'] = true; } elseif (!in_array($membersOnlineOptions['sort'], $allowed_sort_options)) { trigger_error('Sort method for getMembersOnlineStats() function is not allowed', E_USER_NOTICE); } // Initialize the array that'll be returned later on. $membersOnlineStats = array('users_online' => array(), 'list_users_online' => array(), 'online_groups' => array(), 'num_guests' => 0, 'num_spiders' => 0, 'num_buddies' => 0, 'num_users_hidden' => 0, 'num_users_online' => 0); // Get any spiders if enabled. $spiders = array(); $spider_finds = array(); if (!empty($modSettings['show_spider_online']) && ($modSettings['show_spider_online'] < 3 || allowedTo('admin_forum')) && !empty($modSettings['spider_name_cache'])) { $spiders = unserialize($modSettings['spider_name_cache']); } // Load the users online right now. $request = smf_db_query(' SELECT lo.id_member, lo.log_time, lo.id_spider, mem.real_name, mem.member_name, mem.show_online, mem.id_group AS primary_group, mem.id_post_group AS post_group, mem.additional_groups AS secondary_groups, 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_mem_group} THEN mem.id_post_group ELSE mem.id_group END)', array('reg_mem_group' => 0)); $real_team_members = array(); $visible_team_members = array(); while ($row = mysql_fetch_assoc($request)) { if (!empty($modSettings['who_track_team']) && ($row['primary_group'] == 2 || in_array(2, explode(',', $row['secondary_groups'])))) { $real_team_members[] = $row['id_member']; if (($row['show_online'] || $membersOnlineOptions['show_hidden']) && $user_info['id'] != $row['id_member']) { $visible_team_members[] = $row['id_member']; } } if (empty($row['real_name'])) { // Do we think it's a spider? if ($row['id_spider'] && isset($spiders[$row['id_spider']])) { $spider_finds[$row['id_spider']] = isset($spider_finds[$row['id_spider']]) ? $spider_finds[$row['id_spider']] + 1 : 1; $membersOnlineStats['num_spiders']++; } // Guests are only nice for statistics. $membersOnlineStats['num_guests']++; continue; } elseif (empty($row['show_online']) && empty($membersOnlineOptions['show_hidden'])) { // Just increase the stats and don't add this hidden user to any list. $membersOnlineStats['num_users_hidden']++; continue; } $href = URL::user($row['id_member'], $row['real_name']); // Some basic color coding... // Buddies get counted and highlighted. $is_buddy = in_array($row['id_member'], $user_info['buddies']); if ($is_buddy) { $membersOnlineStats['num_buddies']++; } $class = 'member group_' . (empty($row['primary_group']) ? $row['post_group'] : $row['primary_group']) . ($is_buddy ? ' buddy' : ''); 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>'; } // A lot of useful information for each member. $membersOnlineStats['users_online'][$row[$membersOnlineOptions['sort']] . $row['member_name']] = array('id' => $row['id_member'], 'username' => $row['member_name'], 'name' => $row['real_name'], 'group' => $row['primary_group'], 'href' => $href, 'link' => $link, 'is_buddy' => $is_buddy, 'hidden' => empty($row['show_online']), 'is_last' => false); if ($is_buddy) { $membersOnlineStats['buddies_online'][] = $membersOnlineStats['users_online'][$row[$membersOnlineOptions['sort']] . $row['member_name']]['link']; } // This is the compact version, simply implode it to show. $membersOnlineStats['list_users_online'][$row[$membersOnlineOptions['sort']] . $row['member_name']] = empty($row['show_online']) ? '<em>' . $link . '</em>' : $link; if ($row['primary_group'] == 2 || in_array(2, explode(',', $row['secondary_groups']))) { $team_members[] = $row['id_member']; } // Store all distinct (primary) membergroups that are shown. if (!isset($membersOnlineStats['online_groups'][$row['primary_group']])) { $membersOnlineStats['online_groups'][$row['primary_group']] = array('id' => $row['primary_group'], 'name' => $row['group_name']); } } mysql_free_result($request); /* * team members can be cached. reload them when the number of cached team members != the number of * actually online (= a team member logged in or out) */ if (($_team_members = CacheAPI::getCache('_team_members_online', 1200)) == null) { $_team_members = array(); if (!empty($real_team_members)) { $ids = loadMemberData($real_team_members); foreach ($real_team_members as $member) { loadMemberContext($member); $_team_members[$member] =& $memberContext[$member]; } CacheAPI::putCache('_team_members_online', $_team_members, 1200); } else { CacheAPI::putCache('_team_members_online', null, 0); } } $membersOnlineStats['team_members'] =& $_team_members; $membersOnlineStats['visible_team_members'] = $visible_team_members; // If there are spiders only and we're showing the detail, add them to the online list - at the bottom. if (!empty($spider_finds) && $modSettings['show_spider_online'] > 1) { foreach ($spider_finds as $id => $count) { $link = $spiders[$id] . ($count > 1 ? ' (' . $count . ')' : ''); $sort = $membersOnlineOptions['sort'] = 'log_time' && $membersOnlineOptions['reverse_sort'] ? 0 : 'zzz_'; $membersOnlineStats['users_online'][$sort . $spiders[$id]] = array('id' => 0, 'username' => $spiders[$id], 'name' => $link, 'group' => $txt['spiders'], 'href' => '', 'link' => $link, 'is_buddy' => false, 'hidden' => false, 'is_last' => false); $membersOnlineStats['list_users_online'][$sort . $spiders[$id]] = $link; } } // Time to sort the list a bit. if (!empty($membersOnlineStats['users_online'])) { // Determine the sort direction. $sortFunction = empty($membersOnlineOptions['reverse_sort']) ? 'ksort' : 'krsort'; // Sort the two lists. $sortFunction($membersOnlineStats['users_online']); $sortFunction($membersOnlineStats['list_users_online']); // Mark the last list item as 'is_last'. $userKeys = array_keys($membersOnlineStats['users_online']); $membersOnlineStats['users_online'][end($userKeys)]['is_last'] = true; } // Also sort the membergroups. ksort($membersOnlineStats['online_groups']); // Hidden and non-hidden members make up all online members. $membersOnlineStats['num_users_online'] = count($membersOnlineStats['users_online']) + $membersOnlineStats['num_users_hidden'] - (isset($modSettings['show_spider_online']) && $modSettings['show_spider_online'] > 1 ? count($spider_finds) : 0); // output users who were online today if (!empty($modSettings['who_track_daily_visitors']) && !empty($modSettings['online_today'])) { foreach ($modSettings['online_today'] as $member) { if ($member['show_online'] || $membersOnlineOptions['show_hidden']) { $membersOnlineStats['online_today'][] = $member['link']; } } } return $membersOnlineStats; }