1900 && $year < 2200 && $month >= 1 && $month <= 12)
$_SESSION['expanded_stats'][$year][] = $month;
}
elseif (!empty($_REQUEST['collapse']))
{
$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) = $year AND MONTH(date) = $month");
$context['monthly'][$year . sprintf('%02d', $month)]['date'] = array(
'month' => sprintf('%02d', $month),
'year' => $year,
);
return;
}
loadLanguage('Stats');
loadTemplate('Stats');
isAllowedTo('view_stats');
// Build the link tree......
$context['linktree'][] = array(
'url' => $scripturl . '?action=stats',
'name' => $txt['smf_stats_1']
);
$context['page_title'] = $context['forum_name'] . ' - ' . $txt['smf_stats_1'];
$context['show_member_list'] = allowedTo('view_mlist');
// Get averages...
$result = db_query("
SELECT
SUM(posts) AS posts, SUM(topics) AS topics, SUM(registers) AS registers,
SUM(mostOn) AS mostOn, MIN(date) AS date, SUM(hits) AS hits
FROM {$db_prefix}log_activity", __FILE__, __LINE__);
$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'] = round($row['posts'] / $total_days_up, 2);
$context['average_topics'] = round($row['topics'] / $total_days_up, 2);
$context['average_members'] = round($row['registers'] / $total_days_up, 2);
$context['average_online'] = round($row['mostOn'] / $total_days_up, 2);
$context['average_hits'] = round($row['hits'] / $total_days_up, 2);
$context['num_hits'] = $row['hits'];
// How many users are online now.
$result = db_query("
SELECT COUNT(*)
FROM {$db_prefix}log_online", __FILE__, __LINE__);
list ($context['users_online']) = mysql_fetch_row($result);
mysql_free_result($result);
// Statistics such as number of boards, categories, etc.
$result = db_query("
SELECT COUNT(*)
FROM {$db_prefix}boards AS b", __FILE__, __LINE__);
list ($context['num_boards']) = mysql_fetch_row($result);
mysql_free_result($result);
$result = db_query("
SELECT COUNT(*)
FROM {$db_prefix}categories AS c", __FILE__, __LINE__);
list ($context['num_categories']) = mysql_fetch_row($result);
mysql_free_result($result);
$context['num_members'] = &$modSettings['totalMembers'];
$context['num_posts'] = &$modSettings['totalMessages'];
$context['num_topics'] = &$modSettings['totalTopics'];
$context['most_members_online'] = array(
'number' => &$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'] = cache_get_data('stats_gender', 240)) == null)
{
$result = db_query("
SELECT COUNT(*) AS totalMembers, gender
FROM {$db_prefix}members
GROUP BY gender", __FILE__, __LINE__);
$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['totalMembers'];
}
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);
cache_put_data('stats_gender', $context['gender'], 240);
}
$date = strftime('%Y%m%d', forum_time(false));
// Members online so far today.
$result = db_query("
SELECT mostOn
FROM {$db_prefix}log_activity
WHERE date = $date
LIMIT 1", __FILE__, __LINE__);
list ($context['online_today']) = mysql_fetch_row($result);
mysql_free_result($result);
$context['online_today'] = (int) $context['online_today'];
// Poster top 10.
$members_result = db_query("
SELECT ID_MEMBER, realName, posts
FROM {$db_prefix}members
WHERE posts > 0
ORDER BY posts DESC
LIMIT 10", __FILE__, __LINE__);
$context['top_posters'] = array();
$max_num_posts = 1;
while ($row_members = mysql_fetch_assoc($members_result))
{
$context['top_posters'][] = array(
'name' => $row_members['realName'],
'id' => $row_members['ID_MEMBER'],
'num_posts' => $row_members['posts'],
'href' => $scripturl . '?action=profile;u=' . $row_members['ID_MEMBER'],
'link' => '' . $row_members['realName'] . ''
);
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);
// Board top 10.
$boards_result = db_query("
SELECT ID_BOARD, name, numPosts
FROM {$db_prefix}boards AS b
WHERE $user_info[query_see_board]" . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? "
AND b.ID_BOARD != $modSettings[recycle_board]" : '') . "
ORDER BY numPosts DESC
LIMIT 10", __FILE__, __LINE__);
$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['numPosts'],
'href' => $scripturl . '?board=' . $row_board['ID_BOARD'] . '.0',
'link' => '' . $row_board['name'] . ''
);
if ($max_num_posts < $row_board['numPosts'])
$max_num_posts = $row_board['numPosts'];
}
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);
// 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 = db_query("
SELECT ID_TOPIC
FROM {$db_prefix}topics
WHERE numReplies != 0
ORDER BY numReplies DESC
LIMIT 100", __FILE__, __LINE__);
$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 = db_query("
SELECT m.subject, t.numReplies, t.ID_BOARD, t.ID_TOPIC, b.name
FROM ({$db_prefix}topics AS t, {$db_prefix}messages AS m, {$db_prefix}boards AS b)
WHERE m.ID_MSG = t.ID_FIRST_MSG
AND $user_info[query_see_board]" . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? "
AND b.ID_BOARD != $modSettings[recycle_board]" : '') . "
AND t.ID_BOARD = b.ID_BOARD" . (!empty($topic_ids) ? "
AND t.ID_TOPIC IN (" . implode(', ', $topic_ids) . ")" : '') . "
ORDER BY t.numReplies DESC
LIMIT 10", __FILE__, __LINE__);
$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' => '' . $row_topic_reply['name'] . ''
),
'subject' => $row_topic_reply['subject'],
'num_replies' => $row_topic_reply['numReplies'],
'href' => $scripturl . '?topic=' . $row_topic_reply['ID_TOPIC'] . '.0',
'link' => '' . $row_topic_reply['subject'] . ''
);
if ($max_num_replies < $row_topic_reply['numReplies'])
$max_num_replies = $row_topic_reply['numReplies'];
}
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);
// Large forums may need a bit more prodding...
if ($modSettings['totalMessages'] > 100000)
{
$request = db_query("
SELECT ID_TOPIC
FROM {$db_prefix}topics
WHERE numViews != 0
ORDER BY numViews DESC
LIMIT 100", __FILE__, __LINE__);
$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 = db_query("
SELECT m.subject, t.numViews, t.ID_BOARD, t.ID_TOPIC, b.name
FROM ({$db_prefix}topics AS t, {$db_prefix}messages AS m, {$db_prefix}boards AS b)
WHERE m.ID_MSG = t.ID_FIRST_MSG
AND $user_info[query_see_board]" . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? "
AND b.ID_BOARD != $modSettings[recycle_board]" : '') . "
AND t.ID_BOARD = b.ID_BOARD" . (!empty($topic_ids) ? "
AND t.ID_TOPIC IN (" . implode(', ', $topic_ids) . ")" : '') . "
ORDER BY t.numViews DESC
LIMIT 10", __FILE__, __LINE__);
$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' => '' . $row_topic_views['name'] . ''
),
'subject' => $row_topic_views['subject'],
'num_views' => $row_topic_views['numViews'],
'href' => $scripturl . '?topic=' . $row_topic_views['ID_TOPIC'] . '.0',
'link' => '' . $row_topic_views['subject'] . ''
);
if ($max_num_views < $row_topic_views['numViews'])
$max_num_views = $row_topic_views['numViews'];
}
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);
// Try to cache this when possible, because it's a little unavoidably slow.
if (($members = cache_get_data('stats_top_starters', 360)) == null)
{
$request = db_query("
SELECT ID_MEMBER_STARTED, COUNT(*) AS hits
FROM {$db_prefix}topics" . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? "
WHERE ID_BOARD != $modSettings[recycle_board]" : '') . "
GROUP BY ID_MEMBER_STARTED
ORDER BY hits DESC
LIMIT 20", __FILE__, __LINE__);
$members = array();
while ($row = mysql_fetch_assoc($request))
$members[$row['ID_MEMBER_STARTED']] = $row['hits'];
mysql_free_result($request);
cache_put_data('stats_top_starters', $members, 360);
}
if (empty($members))
$members = array(0 => 0);
// Topic poster top 10.
$members_result = db_query("
SELECT ID_MEMBER, realName
FROM {$db_prefix}members
WHERE ID_MEMBER IN (" . implode(', ', array_keys($members)) . ")
ORDER BY FIND_IN_SET(ID_MEMBER, '" . implode(',', array_keys($members)) . "')
LIMIT 10", __FILE__, __LINE__);
$context['top_starters'] = array();
$max_num_topics = 1;
while ($row_members = mysql_fetch_assoc($members_result))
{
$context['top_starters'][] = array(
'name' => $row_members['realName'],
'id' => $row_members['ID_MEMBER'],
'num_topics' => $members[$row_members['ID_MEMBER']],
'href' => $scripturl . '?action=profile;u=' . $row_members['ID_MEMBER'],
'link' => '' . $row_members['realName'] . ''
);
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);
// Time online top 10.
// !!!SLOW This query is sorta slow. Should we just add a key? (or would that be bad in the long run?)
$temp = cache_get_data('stats_total_time_members', 600);
$members_result = db_query("
SELECT ID_MEMBER, realName, totalTimeLoggedIn
FROM {$db_prefix}members" . (!empty($temp) ? "
WHERE ID_MEMBER IN (" . implode(', ', $temp) . ")" : '') . "
ORDER BY totalTimeLoggedIn DESC
LIMIT 20", __FILE__, __LINE__);
$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['totalTimeLoggedIn'] / 86400);
$timeHours = floor(($row_members['totalTimeLoggedIn'] % 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['totalTimeLoggedIn'] % 3600) / 60) . $txt['totalTimeLogged7'];
$context['top_time_online'][] = array(
'id' => $row_members['ID_MEMBER'],
'name' => $row_members['realName'],
'time_online' => $timelogged,
'seconds_online' => $row_members['totalTimeLoggedIn'],
'href' => $scripturl . '?action=profile;u=' . $row_members['ID_MEMBER'],
'link' => '' . $row_members['realName'] . ''
);
if ($max_time_online < $row_members['totalTimeLoggedIn'])
$max_time_online = $row_members['totalTimeLoggedIn'];
}
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)
cache_put_data('stats_total_time_members', $temp2, 480);
// Activity by month.
$months_result = 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(mostOn) AS mostOn, COUNT(*) AS numDays
FROM {$db_prefix}log_activity
GROUP BY stats_year, stats_month", __FILE__, __LINE__);
$context['monthly'] = 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']]);
$context['monthly'][$ID_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 . '#' . $ID_MONTH,
'link' => '' . $txt['months'][$row_months['stats_month']] . ' ' . $row_months['stats_year'] . '',
'month' => $txt['months'][$row_months['stats_month']],
'year' => $row_months['stats_year'],
'new_topics' => $row_months['topics'],
'new_posts' => $row_months['posts'],
'new_members' => $row_months['registers'],
'most_members_online' => $row_months['mostOn'],
'hits' => $row_months['hits'],
'num_days' => $row_months['numDays'],
'days' => array(),
'expanded' => $expanded
);
}
// This gets rid of the filesort on the query ;).
krsort($context['monthly']);
if (empty($_SESSION['expanded_stats']))
return;
$condition = array();
foreach ($_SESSION['expanded_stats'] as $year => $months)
if (!empty($months))
$condition[] = "YEAR(date) = $year AND MONTH(date) IN (" . implode(', ', $months) . ')';
// No daily stats to even look at?
if (empty($condition))
return;
getDailyStats(implode(' OR ', $condition));
}
function getDailyStats($condition)
{
global $context, $db_prefix;
// Activity by day.
$days_result = db_query("
SELECT YEAR(date) AS stats_year, MONTH(date) AS stats_month, DAYOFMONTH(date) AS stats_day, topics, posts, registers, mostOn, hits
FROM {$db_prefix}log_activity
WHERE $condition
ORDER BY stats_day ASC", __FILE__, __LINE__);
while ($row_days = mysql_fetch_assoc($days_result))
$context['monthly'][$row_days['stats_year'] . sprintf('%02d', $row_days['stats_month'])]['days'][] = array(
'day' => sprintf('%02d', $row_days['stats_day']),
'month' => sprintf('%02d', $row_days['stats_month']),
'year' => $row_days['stats_year'],
'new_topics' => $row_days['topics'],
'new_posts' => $row_days['posts'],
'new_members' => $row_days['registers'],
'most_members_online' => $row_days['mostOn'],
'hits' => $row_days['hits']
);
mysql_free_result($days_result);
}
// This is the function which returns stats to simple machines.org IF enabled!
// See http://www.simplemachines.org/about/stats.php for more info.
function SMStats()
{
global $modSettings, $user_info, $forum_version;
// First, is it disabled?
if (empty($modSettings['allow_sm_stats']))
die();
// Are we saying who we are, and are we right? (OR an admin)
if (!$user_info['is_admin'] && (!isset($_GET['sid']) || $_GET['sid'] != $modSettings['allow_sm_stats']))
die();
// Verify the referer...
if (!$user_info['is_admin'] && (!isset($_SERVER['HTTP_REFERER']) || md5($_SERVER['HTTP_REFERER']) != '746cb59a1a0d5cf4bd240e5a67c73085'))
die();
// Get the actual stats.
$stats_to_send = array(
'UID' => $modSettings['allow_sm_stats'],
'time_added' => time(),
'members' => $modSettings['totalMembers'],
'messages' => $modSettings['totalMessages'],
'topics' => $modSettings['totalTopics'],
'boards' => 0,
'php_version' => PHP_VERSION,
'mysql_version' => '',
'smf_version' => $forum_version,
'smfd_version' => $modSettings['smfVersion'],
);
$request = db_query("
SELECT VERSION()", __FILE__, __LINE__);
list ($stats_to_send['mysql_version']) = mysql_fetch_row($request);
mysql_free_result($request);
// Encode all the data, for security.
foreach ($stats_to_send as $k => $v)
$stats_to_send[$k] = urlencode($k) . '=' . urlencode($v);
// Turn this into the query string!
$stats_to_send = implode('&', $stats_to_send);
// If we're an admin, just plonk them out.
if ($user_info['is_admin'])
echo $stats_to_send;
else
{
// Connect to the collection script.
$fp = @fsockopen("www.simplemachines.org", 80, $errno, $errstr);
if ($fp)
{
$length = strlen($stats_to_send);
$out = "POST /smf/stats/collect_stats.php HTTP/1.1\r\n";
$out .= "Host: www.simplemachines.org\r\n";
$out .= "Content-Type: application/x-www-form-urlencoded\r\n";
$out .= "Content-Length: $length\r\n\r\n";
$out .= "$stats_to_send\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
fclose($fp);
}
}
// Die.
die('OK');
}
?>