Files
forum/Sources/Display.php
2014-01-05 13:51:40 +00:00

1045 lines
42 KiB
PHP

<?php
/******************************************************************************
* Display.php *
*******************************************************************************
* SMF: Simple Machines Forum *
* Open-Source Project Inspired by Zef Hemel (zef@zefhemel.com) *
* =========================================================================== *
* Software Version: SMF 1.0.1 *
* Software by: Simple Machines (http://www.simplemachines.org) *
* Copyright 2001-2005 by: Lewis Media (http://www.lewismedia.com) *
* Support, News, Updates at: http://www.simplemachines.org *
*******************************************************************************
* This program is free software; you may redistribute it and/or modify it *
* under the terms of the provided license as published by Lewis Media. *
* *
* This program is distributed in the hope that it is and will be useful, *
* but WITHOUT ANY WARRANTIES; without even any implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
* See the "license.txt" file for details of the Simple Machines license. *
* The latest version can always be found at http://www.simplemachines.org. *
******************************************************************************/
if (!defined('SMF'))
die('Hacking attempt...');
/* This is perhaps the most important and probably most accessed files in all
of SMF. This file controls topic, message, and attachment display. It
does so with the following functions:
void Display()
- loads the posts in a topic up so they can be displayed.
- supports wireless, using wap/wap2/imode and the Wireless templates.
- uses the main sub template of the Display template.
- requires a topic, and can go to the previous or next topic from it.
- jumps to the correct post depending on a number/time/IS_MSG passed.
- depends on the defaultMaxMessages and enableAllMessages settings.
- is accessed by ?topic=ID_TOPIC.START.
array prepareDisplayContext(bool reset = false)
- actually gets and prepares the message context.
- starts over from the beginning if reset is set to true, which is
useful for showing an index before or after the posts.
void Download()
- downloads an attachment or avatar, and increments the downloads.
- requires the view_attachments permission. (not for avatars!)
- disables the session parser, and clears any previous output.
- depends on the attachmentUploadDir setting being correct.
- is accessed via the query string ?action=dlattach.
- views to attachments and avatars do not increase hits and are not
logged in the "Who's Online" log.
array loadAttachmentContext(int ID_MSG)
- loads an attachment's contextual data including, most importantly,
its size if it is an image.
- expects the $attachments array to have been filled with the proper
attachment data, as Display() does.
- requires the view_attachments permission to calculate image size.
- attempts to keep the "aspect ratio" of the posted image in line,
even if it has to be resized by the maxwidth and maxheight settings.
*/
// The central part of the board - topic display.
function Display()
{
global $scripturl, $txt, $db_prefix, $modSettings, $context, $settings, $options, $sourcedir, $mongodb;
global $user_info, $ID_MEMBER, $board_info, $topic, $board, $attachments, $messages_request, $moderatorial_messages;
// What are you gonna display if these are empty?!
if (empty($topic))
fatal_lang_error('smf232', false);
// Load the proper template and/or sub template.
if (WIRELESS)
$context['sub_template'] = WIRELESS_PROTOCOL . '_display';
else
loadTemplate('Display');
$ignores = $mongodb->rock->ignorelists->findOne(array('user' => (int) $ID_MEMBER), array ("ignores" => 1));
$ignores == NULL ? $context['ignore_list'] = array() : $context['ignore_list'] = $ignores['ignores'];
// 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'))
{
// Just prepare some variables that are used in the query.
$gt_lt = $_REQUEST['prev_next'] == 'prev' ? '>' : '<';
$order = $_REQUEST['prev_next'] == 'prev' ? '' : ' DESC';
$request = db_query("
SELECT t2.ID_TOPIC
FROM {$db_prefix}topics AS t, {$db_prefix}topics AS t2, {$db_prefix}messages AS m, {$db_prefix}messages AS m2
WHERE m.ID_MSG = t.ID_LAST_MSG
AND t.ID_TOPIC = $topic" . (empty($modSettings['enableStickyTopics']) ? "
AND m2.posterTime $gt_lt m.posterTime" : "
AND ((m2.posterTime $gt_lt m.posterTime AND t2.isSticky $gt_lt= t.isSticky) OR t2.isSticky $gt_lt t.isSticky)") . "
AND t2.ID_LAST_MSG = m2.ID_MSG
AND t2.ID_BOARD = $board
ORDER BY" . (empty($modSettings['enableStickyTopics']) ? '' : " t2.isSticky$order,") . " m2.posterTime$order
LIMIT 1", __FILE__, __LINE__);
// No more left.
if (mysql_num_rows($request) == 0)
fatal_lang_error('previous_next_end', false);
// 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';
}
$request=db_query("
SELECT t.ID_TOPIC AS id, msg.subject AS subj
FROM {$db_prefix}related_topics rt
INNER JOIN {$db_prefix}topics t ON (t.ID_TOPIC=rt.ID_slave)
INNER JOIN {$db_prefix}messages msg ON (msg.ID_MSG=t.ID_FIRST_MSG)
WHERE rt.ID_master={$context['current_topic']}", __FILE__, __LINE__);
if(mysql_num_rows($request)==0) $context['related_topics']=array();
else
while($context['related_topics'][]=mysql_fetch_assoc($request));
// Add 1 to the number of views of this topic.
if (empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic)
{
db_query("
UPDATE {$db_prefix}topics
SET numViews = numViews + 1
WHERE ID_TOPIC = $topic
LIMIT 1", __FILE__, __LINE__);
$_SESSION['last_read_topic'] = $topic;
}
// Get all the important topic info.
$request = db_query("
SELECT
t.numReplies, t.numViews, t.locked, ms.subject, t.isSticky, t.isFirstSticky, t.ID_POLL,
t.ID_MEMBER_STARTED, IFNULL(lt.logTime, 0) AS logTime, t.ID_FIRST_MSG, t.subtitle AS subtitle
FROM ({$db_prefix}topics AS t, {$db_prefix}messages AS ms)
LEFT JOIN {$db_prefix}log_topics AS lt ON (lt.ID_TOPIC = $topic AND lt.ID_MEMBER = $ID_MEMBER)
WHERE t.ID_TOPIC = $topic
AND ms.ID_MSG = t.ID_FIRST_MSG
LIMIT 1", __FILE__, __LINE__);
if (mysql_num_rows($request) == 0)
fatal_lang_error(472, false);
$topicinfo = mysql_fetch_assoc($request);
mysql_free_result($request);
// The start isn't a number; it's information about what to do, where to go.
if (!is_numeric($_REQUEST['start']))
{
// Redirect to the page and post with new messagesm, originally by Omar Bazavilvazo.
if ($_REQUEST['start'] == 'new')
{
// Guests automatically go to the last topic.
if ($user_info['is_guest'])
$logTime = time();
else
{
// Find the earliest unread message in the topic. (the use of topics here is just for both tables.)
$request = db_query("
SELECT IFNULL(lt.logTime, IFNULL(lmr.logTime, 0)) AS logTime
FROM {$db_prefix}topics AS t
LEFT JOIN {$db_prefix}log_topics AS lt ON (lt.ID_TOPIC = $topic AND lt.ID_MEMBER = $ID_MEMBER)
LEFT JOIN {$db_prefix}log_mark_read AS lmr ON (lmr.ID_BOARD = $board AND lmr.ID_MEMBER = $ID_MEMBER)
WHERE t.ID_TOPIC = $topic
LIMIT 1", __FILE__, __LINE__);
list ($logTime) = mysql_fetch_row($request);
mysql_free_result($request);
}
// Fall through to the next if statement.
$_REQUEST['start'] = 'from' . $logTime;
}
// Start from a certain time index, not a message.
if (substr($_REQUEST['start'], 0, 4) == 'from')
{
// Find the number of messages posted before said time...
$request = db_query("
SELECT COUNT(ID_MSG)
FROM {$db_prefix}messages
WHERE posterTime < " . (int) substr($_REQUEST['start'], 4) . "
AND ID_TOPIC = $topic", __FILE__, __LINE__);
list ($context['start_from']) = mysql_fetch_row($request);
mysql_free_result($request);
// Handle view_newest_first options, and get the correct start value.
$_REQUEST['start'] = empty($options['view_newest_first']) ? $context['start_from'] : $topicinfo['numReplies'] - $context['start_from'];
}
// Link to a message...
elseif (substr($_REQUEST['start'], 0, 3) == 'msg')
{
// Find the start value for that message......
$request = db_query("
SELECT COUNT(ID_MSG)
FROM {$db_prefix}messages
WHERE ID_MSG < " . (int) substr($_REQUEST['start'], 3) . "
AND ID_TOPIC = $topic", __FILE__, __LINE__);
list ($_REQUEST['start']) = mysql_fetch_row($request);
mysql_free_result($request);
// We need to reverse the start as well in this case.
if (!empty($options['view_newest_first']))
$_REQUEST['start'] = $topicinfo['numReplies'] - $_REQUEST['start'];
}
}
// 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">' . $txt['previous_next_back'] . '</a> <a href="' . $scripturl . '?topic=' . $topic . ';prev_next=next">' . $txt['previous_next_forward'] . '</a>' : '';
// Check if spellchecking is both enabled and actually working. (for quick reply.)
$context['show_spellchecking'] = $modSettings['enableSpellChecking'] && function_exists('pspell_new');
// Censor the title...
censorText($topicinfo['subject']);
$context['page_title'] = $topicinfo['subject'];
$context['subtitle'] = $topicinfo['subtitle'];
$context['num_replies'] = $topicinfo['numReplies'];
$context['topic_first_message'] = $topicinfo['ID_FIRST_MSG'];
// Is this topic sticky, or can it even be?
$topicinfo['isSticky'] = empty($modSettings['enableStickyTopics']) ? '0' : $topicinfo['isSticky'];
// Default this topic to not marked for notifications... of course...
$context['is_marked_notify'] = false;
// Guests can't mark topics read or for notifications, just can't sorry.
if (!$user_info['is_guest'])
{
// Mark the topic as read :)
db_query("
REPLACE INTO {$db_prefix}log_topics
(logTime, ID_MEMBER, ID_TOPIC)
VALUES (" . time() . ", $ID_MEMBER, $topic)", __FILE__, __LINE__);
// Check for notifications on this topic OR board.
$request = db_query("
SELECT sent, ID_TOPIC
FROM {$db_prefix}log_notify
WHERE (ID_TOPIC = $topic OR ID_BOARD = $board)
AND ID_MEMBER = $ID_MEMBER
LIMIT 2", __FILE__, __LINE__);
$do_once = true;
while ($row = mysql_fetch_assoc($request))
{
// Find if this topic is marked for notification...
if (!empty($row['ID_TOPIC']))
$context['is_marked_notify'] = true;
// Only do this once, but mark the notifications as "not sent yet" for next time.
if (!empty($row['sent']) && $do_once)
{
db_query("
UPDATE {$db_prefix}log_notify
SET sent = 0
WHERE (ID_TOPIC = $topic OR ID_BOARD = $board)
AND ID_MEMBER = $ID_MEMBER
LIMIT 1", __FILE__, __LINE__);
$do_once = false;
}
}
// Mark board as seen if this is the only new topic.
if (isset($_REQUEST['topicseen']))
{
// Use the mark read tables... and the last visit to figure out if this should be read or not.
$request = db_query("
SELECT COUNT(t.ID_TOPIC)
FROM ({$db_prefix}topics AS t, {$db_prefix}messages AS m)
LEFT JOIN {$db_prefix}log_boards AS lb ON (lb.ID_BOARD = $board AND lb.ID_MEMBER = $ID_MEMBER)
LEFT JOIN {$db_prefix}log_topics AS lt ON (lt.ID_TOPIC = t.ID_TOPIC AND lt.ID_MEMBER = $ID_MEMBER)
WHERE t.ID_BOARD = $board
AND m.ID_MSG = t.ID_LAST_MSG
AND m.posterTime > IFNULL(lt.logTime, IFNULL(lb.logTime, 0))" . (empty($_SESSION['ID_MSG_LAST_VISIT']) ? '' : "
AND t.ID_LAST_MSG > $_SESSION[ID_MSG_LAST_VISIT]"), __FILE__, __LINE__);
list ($numNewTopics) = mysql_fetch_row($request);
mysql_free_result($request);
// If there're no real new topics in this board, mark the board as seen.
if (empty($numNewTopics))
$_REQUEST['boardseen'] = true;
}
// Mark board as seen if we came using last post link from BoardIndex. (or other places...)
if (isset($_REQUEST['boardseen']))
{
db_query("
REPLACE INTO {$db_prefix}log_boards
(logTime, ID_MEMBER, ID_BOARD)
VALUES (" . time() . ", $ID_MEMBER, $board)", __FILE__, __LINE__);
}
}
// Let's get nosey, who is viewing this topic?
if (!empty($settings['display_who_viewing']))
{
// Start out with no one at all viewing it.
$context['view_members'] = array();
$context['view_members_list'] = array();
$context['view_num_hidden'] = 0;
// Search for members who have this topic set in their GET data.
$request = db_query("
SELECT mem.ID_MEMBER, IFNULL(mem.realName, 0) AS realName, mem.showOnline
FROM {$db_prefix}log_online AS lo
LEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = lo.ID_MEMBER)
WHERE lo.url LIKE '%s:5:\"topic\";i:$topic;%'", __FILE__, __LINE__);
while ($row = mysql_fetch_assoc($request))
if (!empty($row['ID_MEMBER']))
{
// Add them both to the list and to the more detailed list.
if (!empty($row['showOnline']) || allowedTo('moderate_forum'))
$context['view_members_list'][] = empty($row['showOnline']) ? '<i><a href="' . $scripturl . '?action=profile;u=' . $row['ID_MEMBER'] . '">' . $row['realName'] . '</a></i>' : '<a href="' . $scripturl . '?action=profile;u=' . $row['ID_MEMBER'] . '">' . $row['realName'] . '</a>';
$context['view_members'][] = array(
'id' => $row['ID_MEMBER'],
'name' => $row['realName'],
'href' => $scripturl . '?action=profile;u=' . $row['ID_MEMBER'],
'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['ID_MEMBER'] . '">' . $row['realName'] . '</a>',
'hidden' => empty($row['showOnline']),
);
if (empty($row['showOnline']))
$context['view_num_hidden']++;
}
// The number of guests is equal to the rows minus the ones we actually used ;).
$context['view_num_guests'] = mysql_num_rows($request) - count($context['view_members']);
mysql_free_result($request);
}
// If all is set, but not allowed... just unset it.
if (isset($_REQUEST['all']) && empty($modSettings['enableAllMessages']))
unset($_REQUEST['all']);
// Otherwise, it must be allowed... so pretend start was -1.
elseif (isset($_REQUEST['all']))
$_REQUEST['start'] = -1;
// Construct the page index, allowing for the .START method...
$context['page_index'] = constructPageIndex($scripturl . '?topic=' . $topic, $_REQUEST['start'], $topicinfo['numReplies'] + 1, $modSettings['defaultMaxMessages'], true);
$context['start'] = $_REQUEST['start'];
// This is information about which page is current, and which page we're on - in case you don't like the constructed page index. (again, wireles..)
$context['page_info'] = array(
'current_page' => floor($_REQUEST['start'] / $modSettings['defaultMaxMessages']) + 1,
'num_pages' => floor($topicinfo['numReplies'] / $modSettings['defaultMaxMessages']) + 1,
);
if (isset ($_REQUEST['topicname'])) {
$context['canonical_url'] = "/forum/{$_REQUEST['topicname']}?start={$_REQUEST['start']}";
} else {
$context['canonical_url'] = $scripturl . '?topic=' . $topic . '.' . $_REQUEST['start'];
}
// Figure out all the link to the next/prev/first/last/etc. for wireless mainly.
$context['links'] = array(
'first' => $_REQUEST['start'] >= $modSettings['defaultMaxMessages'] ? $scripturl . '?topic=' . $topic . '.0' : '',
'prev' => $_REQUEST['start'] >= $modSettings['defaultMaxMessages'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] - $modSettings['defaultMaxMessages']) : '',
'next' => $_REQUEST['start'] + $modSettings['defaultMaxMessages'] < $topicinfo['numReplies'] + 1 ? $scripturl . '?topic=' . $topic. '.' . ($_REQUEST['start'] + $modSettings['defaultMaxMessages']) : '',
'last' => $_REQUEST['start'] + $modSettings['defaultMaxMessages'] < $topicinfo['numReplies'] + 1 ? $scripturl . '?topic=' . $topic. '.' . (floor($topicinfo['numReplies'] / $modSettings['defaultMaxMessages']) * $modSettings['defaultMaxMessages']) : '',
'up' => $scripturl . '?board=' . $board . '.0'
);
// If they are viewing all the posts, show all the posts, otherwise limit the number.
if (!empty($modSettings['enableAllMessages']) && $topicinfo['numReplies'] + 1 > $modSettings['defaultMaxMessages'] && $topicinfo['numReplies'] + 1 < $modSettings['enableAllMessages'])
{
if (isset($_REQUEST['all']))
{
// No limit! (actually, there is a limit, but...)
$modSettings['defaultMaxMessages'] = -1;
$context['page_index'] .= '<div class="b">' . (empty($modSettings['compactTopicPagesEnable']) ? $txt[190] : '[' . $txt[190] . '] ') . '</div>';
// Set start back to 0...
$_REQUEST['start'] = 0;
}
// They aren't using it, but the *option* is there, at least.
else
$context['page_index'] .= '&nbsp;<a href="' . $scripturl . '?topic=' . $topic . '.0;all">' . $txt[190] . '</a> ';
}
// Build the link tree.
/* $context['linktree'][] = array(
'url' => $scripturl . '?topic=' . $topic . '.0',
'name' => $topicinfo['subject'],
'extra_before' => $settings['linktree_inline'] ? $txt[118] . ': ' : ''
);*/
// Build a list of this board's moderators.
$context['moderators'] = &$board_info['moderators'];
$context['link_moderators'] = array();
if (!empty($board_info['moderators']))
{
// Add a link for each moderator...
foreach ($board_info['moderators'] as $mod)
$context['link_moderators'][] = '<a href="' . $scripturl . '?action=profile;u=' . $mod['id'] . '" title="' . $txt[62] . '">' . $mod['name'] . '</a>';
// And show it after the board's name.
//$context['linktree'][count($context['linktree']) - 1]['extra_after'] = ' (' . (count($context['link_moderators']) == 1 ? $txt[298] : $txt[299]) . ': ' . implode(', ', $context['link_moderators']) . ')';
$context['moderators_list'] = (count($context['link_moderators']) == 1 ? $txt[298] : $txt[299]) . ': ' . implode(', ', $context['link_moderators']);
}
// Information about the current topic...
$context['is_locked'] = $topicinfo['locked'];
#$context['is_first_sticky'] = $topicinfo['isFirstSticky'];
$context['is_first_sticky'] = 1;
$context['is_sticky'] = $topicinfo['isSticky'];
$context['is_very_hot'] = $topicinfo['numReplies'] >= $modSettings['hotTopicVeryPosts'];
$context['is_hot'] = $topicinfo['numReplies'] >= $modSettings['hotTopicPosts'];
// We don't want to show the poll icon in the topic class here, so pretend it's not one.
$context['is_poll'] = false;
determineTopicClass($context);
$context['is_poll'] = $topicinfo['ID_POLL'] > 0 && $modSettings['pollMode'] == '1' && allowedTo('poll_view');
// Did this user start the topic or not?
$context['user']['started'] = $ID_MEMBER == $topicinfo['ID_MEMBER_STARTED'] && !$user_info['is_guest'];
// Set the topic's information for the template.
$context['subject'] = $topicinfo['subject'];
$context['num_views'] = $topicinfo['numViews'];
// Create the poll info if it exists.
if ($context['is_poll'])
{
// $pollinfo = eaccelerator_get('__poll-' . $topicinfo['ID_POLL']);
// if ($pollinfo === NULL) {
// Get the question and if it's locked.
$request = db_query("
SELECT
p.question, p.votingLocked, p.hideResults, p.expireTime, p.maxVotes, p.changeVote,
p.ID_MEMBER, IFNULL(mem.realName, p.posterName) AS posterName,
COUNT(DISTINCT lp.ID_MEMBER) AS total, COUNT(lp2.ID_MEMBER) AS has_voted
FROM {$db_prefix}polls AS p
LEFT JOIN {$db_prefix}log_polls AS lp ON (lp.ID_POLL = p.ID_POLL)
LEFT JOIN {$db_prefix}log_polls AS lp2 ON (lp2.ID_POLL = p.ID_POLL AND lp2.ID_MEMBER = $ID_MEMBER)
LEFT JOIN {$db_prefix}members AS mem ON (mem.ID_MEMBER = p.ID_MEMBER)
WHERE p.ID_POLL = $topicinfo[ID_POLL]
GROUP BY p.ID_POLL
LIMIT 1", __FILE__, __LINE__);
$pollinfo = mysql_fetch_assoc($request);
// eaccelerator_put('__poll-' . $topicinfo['ID_POLL'], $topicinfo);
mysql_free_result($request);
// }
// Get all the options, and calculate the total votes.
$request = db_query("
SELECT ID_CHOICE, label, votes
FROM {$db_prefix}poll_choices
WHERE ID_POLL = $topicinfo[ID_POLL]", __FILE__, __LINE__);
$pollOptions = array();
$realtotal = 0;
while ($row = mysql_fetch_assoc($request))
{
$pollOptions[$row['ID_CHOICE']] = array($row['label'], $row['votes']);
$realtotal += $row['votes'];
}
mysql_free_result($request);
// Set up the basic poll information.
$context['poll'] = array(
'id' => $topicinfo['ID_POLL'],
'image' => 'normal_' . (empty($pollinfo['votingLocked']) ? 'poll' : 'locked_poll'),
'question' => doUBBC($pollinfo['question']),
'total_votes' => $pollinfo['total'],
'change_vote' => !empty($pollinfo['changeVote']),
'is_locked' => !empty($pollinfo['votingLocked']),
'options' => array(),
'lock' => allowedTo('poll_lock_any') || ($context['user']['started'] && allowedTo('poll_lock_own')),
'edit' => allowedTo('poll_edit_any') || ($context['user']['started'] && allowedTo('poll_edit_own')),
'allowed_warning' => $pollinfo['maxVotes'] > 1 ? sprintf($txt['poll_options6'], $pollinfo['maxVotes']) : '',
'is_expired' => !empty($pollinfo['expireTime']) && $pollinfo['expireTime'] < time(),
'expire_time' => !empty($pollinfo['expireTime']) ? timeformat($pollinfo['expireTime']) : 0,
'has_voted' => !empty($pollinfo['has_voted']),
'starter' => array(
'id' => $pollinfo['ID_MEMBER'],
'name' => $row['posterName'],
'href' => $pollinfo['ID_MEMBER'] == 0 ? '' : $scripturl . '?action=profile;u=' . $pollinfo['ID_MEMBER'],
'link' => $pollinfo['ID_MEMBER'] == 0 ? $row['posterName'] : '<a href="' . $scripturl . '?action=profile;u=' . $pollinfo['ID_MEMBER'] . '">' . $row['posterName'] . '</a>'
)
);
// You're allowed to vote if:
// 1. the poll did not expire, and
// 2. you're not a guest... and
// 3. you're not trying to view the results, and
// 4. the poll is not locked, and
// 5. you have the proper permissions, and
// 6. you haven't already voted before.
$context['allow_vote'] = !$context['poll']['is_expired'] && !$user_info['is_guest'] && !isset($_REQUEST['viewResults']) && empty($pollinfo['votingLocked']) && allowedTo('poll_vote') && !$context['poll']['has_voted'];
// You're allowed to view the results if:
// 1. You're just a super-nice-guy, or
// 2. Anyone can see them (hideResults == 0), or
// 3. You can see them after you voted (hideResults == 1), or
// 4. You've waited long enough for the poll to expire. (whether hideResults is 1 or 2.)
$context['allow_poll_view'] = allowedTo('moderate_board') || $pollinfo['hideResults'] == 0 || ($pollinfo['hideResults'] == 1 && $context['poll']['has_voted']) || $context['poll']['is_expired'];
// 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.
// 6. the poll creator has said you can!
$context['allow_change_vote'] = !$context['poll']['is_expired'] && !$user_info['is_guest'] && empty($pollinfo['votingLocked']) && allowedTo('poll_vote') && $context['poll']['has_voted'] && $context['poll']['change_vote'];
// Calculate the percentages and bar lengths...
$divisor = $realtotal == 0 ? 1 : $realtotal;
// Determine if a decimal point is needed in order for the options to add to 100%.
$precision = $realtotal == 100 ? 0 : 1;
// Now look through each option, and...
foreach ($pollOptions as $i => $option)
{
// First calculate the percentage, and then the width of the bar...
$bar = round(($option[1] * 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(
'percent' => $bar,
'votes' => $option[1],
'bar' => '<span style="white-space: nowrap;"><img src="' . $settings['images_url'] . '/poll_left.gif" alt="" /><img src="' . $settings['images_url'] . '/poll_middle.gif" width="' . $barWide . '" height="12" alt="-" /><img src="' . $settings['images_url'] . '/poll_right.gif" alt="" /></span>',
'option' => doUBBC($option[0]),
'vote_button' => '<input id="option' . $i . '" type="' . ($pollinfo['maxVotes'] > 1 ? 'checkbox' : 'radio') . '" name="options[]" value="' . $i . '" class="check" />'
);
}
}
// Calculate the fastest way to get the messages.
$ascending = empty($options['view_newest_first']);
$start = $_REQUEST['start'];
$limit = $modSettings['defaultMaxMessages'];
$firstIndex = 0;
if ($start > $topicinfo['numReplies'] / 2 && $modSettings['defaultMaxMessages'] != -1)
{
$ascending = !$ascending;
$limit = $topicinfo['numReplies'] < $start + $limit ? $topicinfo['numReplies'] - $start + 1 : $limit;
$start = $topicinfo['numReplies'] < $start + $limit ? 0 : $topicinfo['numReplies'] - $start - $limit + 1;
$firstIndex = $limit - 1;
}
// Get each post and poster in this topic.
$messages = array();
$posters = array();
// always get post number 0 for sticky topics
if ((($start > 0) || !($ascending)) && ($context['is_sticky'] || $context['is_first_sticky'])) {
$request = db_query("
SELECT ID_MSG, ID_MEMBER
FROM {$db_prefix}messages
WHERE ID_TOPIC = $topic
ORDER BY ID_MSG LIMIT 1", __FILE__, __LINE__);
$row = mysql_fetch_assoc($request);
if (!empty($row['ID_MEMBER']))
$posters[] = $row['ID_MEMBER'];
$messages[] = $row['ID_MSG'];
mysql_free_result($request);
}
// now fetch the rest of the posts
$request = db_query("
SELECT ID_MSG, ID_MEMBER
FROM {$db_prefix}messages
WHERE ID_TOPIC = $topic
ORDER BY ID_MSG " . ($ascending ? '' : 'DESC') . ($modSettings['defaultMaxMessages'] == -1 ? '' : "
LIMIT $start, $limit"), __FILE__, __LINE__);
// declarations moved up
while ($row = mysql_fetch_assoc($request))
{
if (!empty($row['ID_MEMBER']))
$posters[] = $row['ID_MEMBER'];
$messages[] = $row['ID_MSG'];
}
mysql_free_result($request);
$posters = array_unique($posters);
$attachments = array();
// If there _are_ messages here... (probably an error otherwise :!)
if (!empty($messages))
{
// Fetch moderatorial comments
$request = db_query("
SELECT
md.ID_MSG as ID_MSG,
m.realName AS username,
md.level AS level,
md.message AS message,
md.timestamp AS timestamp
FROM {$db_prefix}moderatorial md, {$db_prefix}members m
WHERE
m.ID_MEMBER=md.ID_MEMBER AND
md.ID_MSG IN (" . implode(',', $messages) . ");
", __FILE__, __LINE__);
$moderatorial_messages = array();
while ($row = mysql_fetch_assoc($request)) {
$moderatorial_messages[$row['ID_MSG']] = $row;
}
// Fetch attachments.
if (!empty($modSettings['attachmentEnable']))
{
$request = db_query("
SELECT ID_ATTACH, ID_MSG, filename, IFNULL(size, 0) AS filesize, downloads
FROM {$db_prefix}attachments
WHERE ID_MSG IN (" . implode(',', $messages) . ")", __FILE__, __LINE__);
$temp = array();
while ($row = mysql_fetch_assoc($request))
{
$temp[$row['ID_ATTACH']] = $row;
if (!isset($attachments[$row['ID_MSG']]))
$attachments[$row['ID_MSG']] = array();
}
mysql_free_result($request);
// This is better than sorting it with the query...
ksort($temp);
foreach ($temp as $row)
$attachments[$row['ID_MSG']][] = $row;
}
// What? It's not like it *couldn't* be only guests in this topic...
if (!empty($posters))
loadMemberData($posters);
$messages_request = db_query("
SELECT
ID_MSG, icon, subject, posterTime, posterIP, ID_MEMBER, modifiedTime, modifiedName, body,
smileysEnabled, posterName, posterEmail, nowPlaying,
(GREATEST(posterTime, modifiedTime) > $topicinfo[logTime]) AS isRead
FROM {$db_prefix}messages
WHERE ID_MSG IN (" . implode(',', $messages) . ")
ORDER BY ID_MSG" . (empty($options['view_newest_first']) ? '' : ' DESC'), __FILE__, __LINE__);
// Go to the last message if the given time is beyond the time of the last message.
if (isset($context['start_from']) && $context['start_from'] >= $topicinfo['numReplies'])
$context['start_from'] = $topicinfo['numReplies'];
// Since the anchor information is needed on the top of the page we load these variables beforehand.
$context['first_message'] = isset($messages[$firstIndex]) ? $messages[$firstIndex] : $messages[0];
if (empty($options['view_newest_first']))
$context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $context['start_from'];
else
$context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $topicinfo['numReplies'] - $context['start_from'];
}
else
{
$messages_request = false;
$context['first_message'] = 0;
$context['first_new_message'] = false;
}
// Set the callback. (do you REALIZE how much memory all the messages would take?!?)
$context['get_message'] = 'prepareDisplayContext';
// Check if the topic is been watched by user
if ($user_info['is_guest']) {
$context['is_watched'] = false;
} else {
$result = db_query("
SELECT watch FROM {$db_prefix}watch_topics
WHERE
ID_MEMBER='{$ID_MEMBER}' AND
ID_TOPIC='{$topic}'",
__FILE__, __LINE__);
if(mysql_num_rows($result) == 0) {
$context['is_watched'] = false;
} else {
list($w) = mysql_fetch_row($result);
$context['is_watched'] = (bool) $w;
}
}
// Basic settings.... may be converted over at some point.
$context['allow_hide_email'] = !empty($modSettings['allow_hideEmail']) || ($user_info['is_guest'] && !empty($modSettings['guest_hideContacts']));
// Now set all the wonderful, wonderful permissions... like moderation ones...
$common_permissions = array(
'can_sticky' => 'make_sticky',
'can_merge' => 'merge_any',
'can_split' => 'split_any',
'calendar_post' => 'calendar_post',
'can_mark_notify' => 'mark_any_notify',
'can_send_topic' => 'send_topic',
'can_send_pm' => 'pm_send',
'can_report_moderator' => 'report_any',
'can_moderate_forum' => 'moderate_forum'
);
foreach ($common_permissions as $contextual => $perm)
$context[$contextual] = allowedTo($perm);
// Permissions with _any/_own versions. $context[YYY] => ZZZ_any/_own.
$anyown_permissions = array(
'can_move' => 'move',
'can_lock' => 'lock',
'can_delete' => 'delete',
'can_add_poll' => 'poll_add',
'can_remove_poll' => 'poll_remove',
'can_reply' => 'post_reply',
);
foreach ($anyown_permissions as $contextual => $perm)
$context[$contextual] = allowedTo($perm . '_any') || ($context['user']['started'] && allowedTo($perm . '_own'));
// Cleanup all the permissions with extra stuff...
$context['can_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_warn'] = allowedTo('visual_warn_any');
// Start this off for quick moderation - it will be or'd for each post.
$context['can_remove_post'] = allowedTo('remove_any') || (allowedTo('remove_replies') && $context['user']['started']);
// Load the "Jump to" list...
loadJumpTo();
// Load up the "double post" sequencing magic.
if (!empty($options['display_quick_reply']))
checkSubmitOnce('register');
}
// Callback for the message display.
function prepareDisplayContext($reset = false)
{
global $settings, $txt, $modSettings, $scripturl, $options;
global $themeUser, $context, $messages_request, $topic, $ID_MEMBER, $attachments, $moderatorial_messages;
static $counter = null;
// If the query returned false, bail.
if ($messages_request == false)
return false;
// Remember which message this is. (ie. reply #83)
if ($counter === null || $reset)
$counter = empty($options['view_newest_first']) ? $context['start'] : $context['num_replies'] - $context['start'];
// Start from the beginning...
if ($reset)
return @mysql_data_seek($messages_request, 0);
// Attempt to get the next message.
$message = mysql_fetch_assoc($messages_request);
if (!$message)
return false;
// If you're a lazy bum, you probably didn't give a subject...
$message['subject'] = $message['subject'] != '' ? $message['subject'] : $txt[24];
// Are you allowed to remove at least a single reply?
$context['can_remove_post'] |= allowedTo('remove_own') && $message['ID_MEMBER'] == $ID_MEMBER;
// If it couldn't load, or the user was a guest.... someday may be done with a guest table.
if (!loadMemberContext($message['ID_MEMBER']))
{
// Notice this information isn't used anywhere else....
$themeUser[$message['ID_MEMBER']]['name'] = $message['posterName'];
$themeUser[$message['ID_MEMBER']]['id'] = 0;
$themeUser[$message['ID_MEMBER']]['group'] = $txt[28];
$themeUser[$message['ID_MEMBER']]['link'] = $message['posterName'];
$themeUser[$message['ID_MEMBER']]['email'] = $message['posterEmail'];
$themeUser[$message['ID_MEMBER']]['is_guest'] = true;
}
else
$themeUser[$message['ID_MEMBER']]['can_view_profile'] = allowedTo('profile_view_any') || ($message['ID_MEMBER'] == $ID_MEMBER && allowedTo('profile_view_own'));
$themeUser[$message['ID_MEMBER']]['ip'] = $message['posterIP'];
// Do the censor thang.
censorText($message['body']);
censorText($message['subject']);
censorText($message['nowPlaying']);
// Run BBC interpreter on the message.
$message['body'] = doUBBC($message['body'], $message['smileysEnabled']);
// Compose the memory eat- I mean message array.
$output = array(
'attachment' => loadAttachmentContext($message['ID_MSG']),
'alternate' => $counter % 2,
'id' => $message['ID_MSG'],
'href' => $scripturl . '?topic=' . $topic . '.msg' . $message['ID_MSG'] . '#msg' . $message['ID_MSG'],
'link' => '<a href="' . $scripturl . '?topic=' . $topic . '.msg' . $message['ID_MSG'] . '#msg' . $message['ID_MSG'] . '">' . $message['subject'] . '</a>',
'member' => &$themeUser[$message['ID_MEMBER']],
'icon' => $message['icon'],
'subject' => $message['subject'],
'time' => timeformat($message['posterTime']),
'timestamp' => $message['posterTime'],
'counter' => $counter,
'modified' => array(
'time' => timeformat($message['modifiedTime']),
'timestamp' => $message['modifiedTime'],
'name' => $message['modifiedName']
),
'body' => $message['body'],
'nowplaying' => $message['nowPlaying'],
'new' => empty($message['isRead']),
'first_new' => isset($context['start_from']) && $context['start_from'] == $counter,
'can_modify' => allowedTo('modify_any') || (allowedTo('modify_replies') && $context['user']['started']) || (allowedTo('modify_own') && $message['ID_MEMBER'] == $ID_MEMBER && $message['posterTime'] > time() - 3600*24),
'can_remove' => allowedTo('remove_any') || (allowedTo('remove_replies') && $context['user']['started']) || (allowedTo('remove_own') && $message['ID_MEMBER'] == $ID_MEMBER),
'can_see_ip' => allowedTo('moderate_forum') || ($message['ID_MEMBER'] == $ID_MEMBER && !empty($ID_MEMBER)),
'moderatorial' => isset($moderatorial_messages[$message['ID_MSG']]) ? $moderatorial_messages[$message['ID_MSG']] : NULL,
);
if (empty($options['view_newest_first']))
$counter++;
else
$counter--;
return $output;
}
// Download an attachment.
function Download()
{
global $txt, $modSettings, $db_prefix, $user_info, $scripturl, $context;
// Make sure some attachment was requested!
if (!isset($_REQUEST['id']))
fatal_lang_error(1, false);
$_REQUEST['id'] = (int) $_REQUEST['id'];
if (isset($_REQUEST['type']) && $_REQUEST['type'] == 'avatar')
{
$request = db_query("
SELECT filename, ID_ATTACH
FROM {$db_prefix}attachments
WHERE ID_ATTACH = $_REQUEST[id]
AND ID_MEMBER > 0
LIMIT 1", __FILE__, __LINE__);
$_REQUEST['image'] = true;
}
// This is just a regular attachment...
else
{
isAllowedTo('view_attachments');
// Make sure this attachment is on this board.
$request = db_query("
SELECT a.filename, a.ID_ATTACH
FROM {$db_prefix}boards AS b, {$db_prefix}messages AS m, {$db_prefix}attachments AS a
WHERE b.ID_BOARD = m.ID_BOARD
AND $user_info[query_see_board]
AND m.ID_MSG = a.ID_MSG
AND a.ID_ATTACH = $_REQUEST[id]
LIMIT 1", __FILE__, __LINE__);
}
if (mysql_num_rows($request) == 0)
fatal_lang_error(1, false);
list ($real_filename, $ID_ATTACH) = mysql_fetch_row($request);
mysql_free_result($request);
// Update the download counter.
db_query("
UPDATE {$db_prefix}attachments
SET downloads = downloads + 1
WHERE ID_ATTACH = $ID_ATTACH", __FILE__, __LINE__);
// This is done to clear any output that was made before now. (would use ob_clean(), but that's PHP 4.2.0+...)
ob_end_clean();
if (!empty($modSettings['enableCompressedOutput']) && @version_compare(PHP_VERSION, '4.2.0') >= 0)
@ob_start('ob_gzhandler');
else
ob_start();
$filename = getAttachmentFilename($real_filename, $_REQUEST['id']);
$thumb_filename = getThumbnailFilename($real_filename, $_REQUEST['id']);
// No point in a nicer message, because this is supposed to be an attachment anyway...
if (!file_exists($filename))
{
loadLanguage('Errors');
header('HTTP/1.0 404 ' . $txt['attachment_not_found']);
header('Content-Type: text/plain');
die('404 - ' . $txt['attachment_not_found']);
}
// Send the attachment headers.
header('Pragma: ');
header('Cache-Control: max-age=' . (525600 * 60) . ', private');
if (!$context['browser']['is_gecko'])
header('Content-Transfer-Encoding: binary');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($filename)) . ' GMT');
header('Accept-Ranges: bytes');
header('Set-Cookie:');
header('Connection: close');
if (!(isset($_REQUEST['image'])||isset($_REQUEST['thumb'])))
{
header('Content-Disposition: attachment; filename="' . $real_filename . '"');
header('Content-Type: application/octet-stream');
}
if (filesize($filename) != 0)
{
$size = @getimagesize($filename);
if (!empty($size) && $size[2] > 0 && $size[2] < 4)
header('Content-Type: image/' . ($size[2] != 1 ? ($size[2] != 2 ? 'png' : 'jpeg') : 'gif'));
}
if (isset($_REQUEST['thumb']))
{
if(!file_exists($thumb_filename))
{
list($width, $height, $type) = @getimagesize($filename);
$type=($type != 1 ? ($type != 2 ? 'png' : 'jpeg') : 'gif');
eval("\$img=imagecreatefrom$type(\$filename);");
$width_orig=$width; $height_orig=$height;
if ($height > 240 || $width > 320)
{
if ($width > 320 )
{
$height = floor(320 / $width * $height);
$width = 320;
if ($height > 240)
{
$width = floor(240 / $height * $width);
$height = 240;
}
}
elseif ($height > 240)
{
$width = floor(240 / $height * $width);
$height = 240;
}
$thumb = imagecreatetruecolor($width, $height);
imagecopyresampled($thumb,$img, 0,0,0,0,$width,$height,$width_orig,$height_orig);
eval("image$type(\$thumb,\$thumb_filename);");
}
}
$filename=$thumb_filename;
}
if (empty($modSettings['enableCompressedOutput']))
header('Content-Length: ' . filesize($filename));
// Try to buy some time...
@set_time_limit(0);
@ini_set('memory_limit', '128M');
// On some of the less-bright hosts, readfile() is disabled. It's just a faster, more byte safe, version of what's in the if.
if (@readfile($filename) === null)
echo implode('', file($filename));
obExit(false);
}
function loadAttachmentContext($ID_MSG)
{
global $attachments, $modSettings, $txt, $scripturl, $topic;
// Set up the attachment info - based on code by Meriadoc.
$attachmentData = array();
if (isset($attachments[$ID_MSG]) && !empty($modSettings['attachmentEnable']))
{
foreach ($attachments[$ID_MSG] as $i => $attachment)
{
$attachmentData[$i] = array(
'name' => $attachment['filename'],
'downloads' => $attachment['downloads'],
'size' => round($attachment['filesize'] / 1024, 2) . ' ' . $txt['smf211'],
'byte_size' => $attachment['filesize'],
'href' => $scripturl . '?action=dlattach;topic=' . $topic . '.0;id=' . $attachment['ID_ATTACH'],
'link' => '<a href="' . $scripturl . '?action=dlattach;topic=' . $topic . '.0;id=' . $attachment['ID_ATTACH'] . '">' . $attachment['filename'] . '</a>',
'is_image' => false
);
if (empty($modSettings['attachmentShowImages']))
continue;
// Set up the image attachment info.
$filename = getAttachmentFilename($attachment['filename'], $attachment['ID_ATTACH']);
$imageTypes = array('gif' => 1, 'jpeg' => 2, 'png' => 3, 'bmp' => 6);
if (file_exists($filename) && filesize($filename) > 0)
list ($width, $height, $imageType) = @getimagesize($filename);
else
{
$imageType = 0;
$attachmentData[$i]['size'] = '0 ' . $txt['smf211'];
}
// If this isn't an image, we're done.
if (!allowedTo('view_attachments') || !in_array($imageType, $imageTypes))
continue;
/*
if(!file_exists($modSettings['attachmentUploadDir'].'/thumbs/'.
// Start resize/restrict posted images mod by Mostmaster.
if (!(empty($modSettings['maxwidth']) && empty($modSettings['maxheight'])) && ($width > $modSettings['maxwidth'] || $height > $modSettings['maxheight']))
{
if ($width > $modSettings['maxwidth'] && !empty($modSettings['maxwidth']))
{
$height = floor($modSettings['maxwidth'] / $width * $height);
$width = $modSettings['maxwidth'];
if ($height > $modSettings['maxheight'] && !empty($modSettings['maxheight']))
{
$width = floor($modSettings['maxheight'] / $height * $width);
$height = $modSettings['maxheight'];
}
}
elseif ($height > $modSettings['maxheight'] && !empty($modSettings['maxheight']))
{
$width = floor($modSettings['maxheight'] / $height * $width);
$height = $modSettings['maxheight'];
}
$attachmentData[$i]['image'] = '<img src="' . $attachmentData[$i]['href'] . ';image" alt="" width="' . $width . '" height="' . $height . '" border="0" />';
}
else*/
$attachmentData[$i]['image'] = '<img src="' . $attachmentData[$i]['href'] . ';thumb" alt="" border="0"/>';
// End resize/restrict posted images mod by Mostmaster.
$attachmentData[$i]['width'] = $width;
$attachmentData[$i]['height'] = $height;
$attachmentData[$i]['is_image'] = true;
$attachmentData[$i]['downloads']++;
}
}
return $attachmentData;
}
?>