\n";
// Search
echo "
";
echo " | \n";
if ($user) {
echo "\n";
echo " Private messages: ", pm_notification($user);
echo " | \n";
}
echo "\n";
end_table();
}
// Output the forum/thread title.
//
function show_forum_title($category, $forum, $thread) {
if ($category) {
$is_helpdesk = $category->is_helpdesk;
} else {
$is_helpdesk = false;
}
$where = $is_helpdesk?tra("Questions and Answers"):tra("Message boards");
$top_url = $is_helpdesk?"forum_help_desk.php":"forum_index.php";
if (!$forum && !$thread) {
echo "$where";
} else if ($forum && !$thread) {
echo "";
echo "$where : ";
echo $forum->title;
echo "";
} else if ($forum && $thread) {
echo "";
echo "$where : ";
echo "id."\">", $forum->title, " : ";
echo cleanup_title($thread->title);
echo "";
} else {
echo "Invalid input to show_forum_title
";
echo "threadid $thread->id";
// TODO: handle this condition gracefully
}
}
function show_team_forum_title($forum, $thread=null) {
$team = BoincTeam::lookup_id($forum->category);
echo "id>$team->name message board
";
if ($thread) {
echo " : id>$thread->title";
}
}
// Start the forum table, output the proper headings and such.
//
function start_forum_table($headings, $extra="width=\"100%\"") {
$span = null;
start_table($extra." cellspacing=\"0\"");
echo "";
for ($i=0; $i$title";
}
echo "
\n";
}
// return a string containing a paged navigation bar
// for the given forum. The default start place is 0.
//
function show_page_nav($forum, $start=0){
// How many pages to potentially show before and after this one:
$preshow = 5; $postshow = 10;
$navbar = "";
if ($forum->threads > THREADS_PER_PAGE) {
$total = ceil($forum->threads / THREADS_PER_PAGE);
$curpage = ceil($start / THREADS_PER_PAGE);
// If this is not the first page, display "previous"
if ($curpage > 0){
$navbar = ' <-- Previous ';
}
// Display a list of pages surrounding this one
for ($i=$curpage-$preshow;$i<($curpage+$postshow);$i++){
if ($i<1) continue;
if ($i>$total) break;
// If this is the current page, emphasize it.
if ($i == $curpage+1){
$navbar.="";
}
$navbar.=''.$i.' | ';
if ($i == $curpage+1){$navbar.="";}
}
// If there is a next page
if ($curpage+1 < $total){
$navbar.= ' Next -->';
}
}
return $navbar;
}
function thread_last_visit($user, $thread) {
if (!$user) return false;
$log = BoincForumLogging::lookup($user->id, $thread->id);
return $log->timestamp;
}
function thread_is_unread($user, $thread) {
if (!$user) return false;
$log = BoincForumLogging::lookup($user->id, $thread->id);
if ($thread->timestamp < $log->timestamp) return false;
if ($thread->timestamp < $user->prefs->mark_as_read_timestamp) return false;
return true;
}
// Process a user-supplied title to remove HTML stuff
//
function cleanup_title($title) {
$x = trim(htmlspecialchars($title));
$x = stripslashes($x); // clean up funky old titles in DB
if (strlen($x)==0) return "(no title)";
else return $x;
}
function can_reply($thread, $forum, $user) {
if ($thread->locked) {
if (!is_moderator($user, $forum)) return false;
}
return true;
}
function should_show_post(
$logged_in_user, $postcount, $postnumber, $post, $sort_style, $last_visit
){
$no_wraparound = get_str("nowrap",true);
// If no user logged in, display everything (useful for Google bots etc)
//
if (!$logged_in_user) return true;
// If the user hasn't enabled the feature to display only
// a certain amount of posts, simply display all of them.
//
if (!$logged_in_user->prefs->minimum_wrap_postcount) return true;
// If the user has asked to display all the posts just display them
//
if ($no_wraparound) return true;
// If it is enabled and we havent yet hit the limit, simply display all
if ($postcount < $logged_in_user->prefs->minimum_wrap_postcount) return true;
// display the post if it is the first post
//
if ($postnumber==1 || $postnumber==($postcount+1)) return true;
// if this post is unread, show it
//
if ($post->timestamp > $last_visit) return true;
// The user can choose to display the last X posts in a thread only,
// "last" here depends on sorting criteria:
//
if ($sort_style==CREATE_TIME_OLD){
if ($postnumber > $postcount+1-$logged_in_user->prefs->display_wrap_postcount) return true;
} else if (CREATE_TIME_NEW){
if ($postnumber <= $logged_in_user->prefs->display_wrap_postcount) return true;
} else {
// For other sorting criteria we do not know how to define "last"
// so simply display it all
//
return true;
}
// In all other cases we should not show this particular post
return false;
}
// Show the posts in a thread for a user.
//
function show_posts(
$thread, $forum, $sort_style, $filter, $logged_in_user, $show_controls=true
) {
$n = 1;
$first_unread_post = null;
$last_visit = 0;
if ($show_controls) {
$controls = FORUM_CONTROLS;
} else {
$controls = NO_CONTROLS;
}
// If logged in user is moderator,
// let him see all posts - including hidden ones
//
if (is_moderator($logged_in_user, $forum)) {
$show_hidden = true;
} else {
$show_hidden = false;
}
$posts = get_thread_posts($thread->id, $sort_style, $show_hidden);
$postcount = (sizeof($posts)-1);
if ($logged_in_user) {
$last_visit = thread_last_visit($logged_in_user, $thread);
}
$postnumber=0; $previous_post=0;
$no_wraparound = get_str("nowrap",true);
foreach ($posts as $post){
$postnumber++;
if (should_show_post(
$logged_in_user, $postcount, $postnumber, $post,
$sort_style, $last_visit)
) {
if ($postnumber!=$previous_post+1){
// A number of posts were hidden, display a way to unhide them:
//
echo " |
Only the first post and the last ".($logged_in_user->prefs->display_wrap_postcount)." posts
(of the ".($postcount+1)." posts in this thread) are displayed.
id."&nowrap=true\">Click here to also display the remaining posts. |
";
}
$previous_post=$postnumber;
show_post($post, $thread, $forum, $logged_in_user, $last_visit, $n, $controls, $filter);
$n = ($n+1)%2;
if (($post->timestamp>$last_visit) &&
((!$first_unread_post) || ($post->timestamp<$first_unread_post->timestamp))
){
$first_unread_post=$post;
}
}
}
if ($logged_in_user && $logged_in_user->prefs->jump_to_unread){
if ($first_unread_post){
echo "";
} else {
echo "";
}
}
if ($logged_in_user) {
BoincForumLogging::replace($logged_in_user->id, $thread->id, time());
}
}
function get_ignored_list($user) {
return explode("|", $user->prefs->ignorelist);
}
function add_ignored_user($user, $other_user) {
$list = explode("|", $user->prefs->ignorelist);
foreach ($list as $key=>$userid) {
if ($userid == $other_user->id) {
return true;
}
}
$list[] = $other_user->id;
$x = implode("|", array_values($list));
return $user->prefs->update("ignorelist='$x'");
}
function remove_ignored_user($user, $other_user) {
$list = explode("|", $user->prefs->ignorelist);
foreach ($list as $key=>$userid) {
if ($userid == $other_user->id) {
unset($list[$key]);
}
}
$x = implode("|", array_values($list));
return $user->prefs->update("ignorelist='$x'");
}
function is_ignoring($user, $other_user) {
$list = explode("|", $user->prefs->ignorelist);
return in_array($other_user->id, $list);
}
function get_output_options($user) {
if (!$user) return null;
$options = new output_options();
if ($user->prefs->images_as_links) $options->images_as_links = 1;
if ($user->prefs->link_popup) $options->link_popup = 1;
return $options;
}
// Display an individual post
//
function show_post(
$post, $thread, $forum, $logged_in_user, $last_visit, $n, $controls=FORUM_CONTROLS, $filter=true
) {
$user = BoincUser::lookup_id($post->user);
BoincForumPrefs::lookup($user);
$config = get_config();
$no_forum_rating = parse_bool($config, "no_forum_rating");
$tokens = "";
$rated_below_threshold = false;
$rated_above_threshold = false;
$options = get_output_options($logged_in_user);
//If the user that made this post is on the list of people to ignore,
// change thresholds to be much more strict
//
if ($logged_in_user){
$tokens = url_tokens($logged_in_user->authenticator);
if (is_ignoring($logged_in_user, $user)){
$user_is_on_ignorelist=true;
$rated_below_threshold = ($logged_in_user->prefs->high_rating_threshold>$post->rating());
$rated_above_threshold = ($logged_in_user->prefs->high_rating_threshold+abs($logged_in_user->prefs->low_rating_threshold)<($post->rating));
} else { //Use normal threshold values
$rated_below_threshold = ($logged_in_user->prefs->low_rating_threshold>($post->rating()));
$rated_above_threshold = ($logged_in_user->prefs->high_rating_threshold<($post->rating));
}
}
// The creator can edit the post, but only in a specified amount of time
// (exception: a moderator can edit his/her posts at any time)
//
$can_edit = false;
if ($logged_in_user) {
if ($user->id == $logged_in_user->id) {
if (is_moderator($logged_in_user, $forum)) {
$can_edit = true;
} else if (can_reply($thread, $forum, $logged_in_user)) {
$time_limit = $post->timestamp+MAXIMUM_EDIT_TIME;
$can_edit = time()<$time_limit;
} else {
$can_edit = false;
}
}
}
echo "
id."\">
";
// Print the user links
echo user_links($user, URL_BASE);
echo " ";
// Print the special user lines, if any
global $special_user_bitfield;
$fstatus="";
$keys = array_keys($special_user_bitfield);
for ($i=0; $i prefs->privilege($keys[$i])) {
$fstatus.=$special_user_bitfield[$keys[$i]]." ";
}
}
if ($user->create_time > time()-ST_NEW_TIME) $fstatus.=ST_NEW." ";
if ($fstatus) echo "$fstatus";
echo "";
if (!$filter || !$rated_below_threshold){
if ($user->prefs->avatar!="" && (!$logged_in_user || ($logged_in_user->prefs->hide_avatars==false))) {
echo "prefs->avatar."\" alt=\"Avatar\"> ";
}
}
$url = "pm.php?action=new&userid=".$user->id;
$name = $user->name;
show_button($url, "Send message", "Send $name a private message");
echo " Joined: ", gmdate('M j y', $user->create_time), " ";
if (!isset($user->nposts)) {
$user->nposts = BoincPost::count("user=$user->id");
}
if(function_exists('project_forum_user_info')){
project_forum_user_info($user);
} else { // default
// circumvent various forms of identity spoofing
// by displaying the user id of the poster.
//
echo "Posts: $user->nposts ";
echo "ID: ".$user->id." ";
echo "Credit: ".number_format($user->total_credit)." ";
echo "RAC: ".number_format($user->expavg_credit)." ";
}
echo " | ";
if ($controls == FORUM_CONTROLS) {
echo "
";
echo "";
if (!$filter || !$rated_below_threshold){
$posttext = $post->content;
// If the creator of this post has a signature and
// wants it to be shown for this post AND the logged in
// user has signatures enabled: show it
//
if ($post->signature && (!$logged_in_user || !$logged_in_user->prefs->hide_signatures)){
$posttext.="\n____________\n".$user->prefs->signature;
}
$posttext = output_transform($posttext, $options);
echo " ", $posttext, " ";
echo " |
";
} else {
$rating = $post->rating();
echo " | Rating: ", $rating, " | rate:
id."&choice=p$tokens\">
";
show_image(RATE_POSITIVE_IMAGE, "Click if you like this message", RATE_POSITIVE_IMAGE_HEIGHT);
echo " / id."&choice=n$tokens\">";
show_image(RATE_NEGATIVE_IMAGE, "Click if you don't like this message", RATE_NEGATIVE_IMAGE_HEIGHT);
echo " id."\">";
show_image(REPORT_POST_IMAGE, "Report this post as offensive", REPORT_POST_IMAGE_HEIGHT);
echo " ";
}
if (($controls == FORUM_CONTROLS) && (can_reply($thread, $forum, $logged_in_user))) {
echo "";
} else {
echo "";
}
echo "
";
} else {
echo "|
|
";
}
// Show a post and its context (e.g. for search results, user posts)
//
function show_post_and_context($post, $thread, $forum, $options, $n) {
$thread = BoincThread::lookup_id($post->thread);
$forum = BoincForum::lookup_id($thread->forum);
$content = output_transform($post->content, $options);
$when = time_diff_str($post->timestamp, time());
$user = lookup_user_id($post->user);
$title = cleanup_title($thread->title);
$m = $n%2;
if ($post->hidden) {
$deleted = "
[Hidden by a moderator]";
} else {
$deleted = "";
}
echo "
$n)
";
switch ($forum->parent_type) {
case 0:
$category = BoincCategory::lookup_id($forum->category);
show_forum_title($category, $forum, $thread);
break;
case 1:
show_team_forum_title($forum);
break;
}
echo "
(id."&nowrap=true#".$post->id."\">Message ".$post->id.")
Posted $when by ".user_links($user)." $deleted
$content
|
";
}
function check_banished($user) {
$x = $user->prefs->banished_until;
if ($x>time()) {
error_page(
"You may not post or rate messages until ".gmdate('M j, Y', $x)
);
}
}
function post_rules() {
return "
- Posts must be 'kid friendly': they may not contain
content that is obscene, hate-related,
sexually explicit or suggestive.
- No commercial advertisements.
- No links to web sites involving sexual content,
gambling, or intolerance of others.
- No messages intended to annoy or antagonize other people,
or to hijack a thread.
- No messages that are deliberately hostile or insulting.
- No abusive comments involving race, religion,
nationality, gender, class or sexuality.
";
}
function post_warning() {
return "
";
}
// Various functions for adding/hiding/unhiding stuff.
// These take care of counts and timestamps.
// Don't do these things directly - use these functions
//
function create_post($content, $parent_id, $user, $forum, $thread, $signature) {
$content = substr($content, 0, 64000);
$content = mysql_real_escape_string($content);
$now = time();
$sig = $signature?1:0;
$id = BoincPost::insert("(thread, user, timestamp, content, parent_post, signature) values ($thread->id, $user->id, $now, '$content', $parent_id, $sig)");
if (!$id) return null;
// send emails to subscribed users
//
$subs = BoincSubscription::enum("threadid=$thread->id");
foreach ($subs as $sub) {
if ($user->id == $sub->userid) continue;
$user2 = BoincUser::lookup_id($sub->userid);
$visit_time = thread_last_visit($user2, $thread);
if ($visit_time > $sub->notified_time) {
send_reply_notification_email($thread, $user2);
}
}
$user->update("posts=posts+1");
$thread->update("replies=replies+1, timestamp=$now");
$forum->update("posts=posts+1, timestamp=$now");
}
function create_thread($title, $content, $user, $forum, $signature) {
$title = trim($title);
$title = strip_tags($title);
$title = mysql_real_escape_string($title);
$now = time();
$id = BoincThread::insert("(forum, owner, title, create_time, timestamp, replies) values ($forum->id, $user->id, '$title', $now, $now, -1)");
if (!$id) return null;
$thread = BoincThread::lookup_id($id);
create_post($content, 0, $user, $forum, $thread, $signature);
$forum->update("threads=threads+1");
return $thread;
}
function hide_post($post, $thread, $forum) {
$post->update("hidden=1");
$thread->update("replies=replies-1");
return $forum->update("posts=posts-1");
}
function unhide_post($post, $thread, $forum) {
$now = time();
$post->update("hidden=0");
$thread->update("replies=replies+1, timestamp=$now");
return $forum->update("posts=posts+1, timestamp=$now");
}
function move_post($post, $old_thread, $old_forum, $new_thread, $new_forum) {
$now = time();
$post->update("thread=$new_thread->id");
$old_thread->update("replies=replies-1");
$new_thread->update("replies=replies+1, timestamp=$now");
$old_forum->update("posts=posts-1");
return $new_forum->update("posts=posts+1, timestamp=$now");
}
function hide_thread($thread, $forum) {
$thread->update("hidden=1");
$forum->update("threads=threads-1");
return $forum->update("posts=posts-$thread->replies-1");
}
function unhide_thread($thread, $forum) {
$now = time();
$thread->update("hidden=0");
return $forum->update("threads=threads+1, posts=posts+$thread->replies+1, timestamp=$now");
}
function move_thread($thread, $old_forum, $new_forum) {
$now = time();
$old_forum->update("threads=threads-1, posts=posts-$thread->replies-1");
$new_forum->update("threads=threads+1, posts=posts+$thread->replies+1, timestamp=$now");
return $thread->update("forum=$new_forum->id");
}
// $show_hidden: 1 if it is a moderator reading
// Error page if this function returns NULL.
// $forumID - int
// $min - int
// $nRec - int
// $sort_style - string (checked by switch statement)
// $show_hidden - bool (not directly passed to SQL)
// $sticky - bool (not directly passed to SQL)
//
function get_forum_threads(
$forumID, $start=-1, $nRec=-1, $sort_style=MODIFIED_NEW,
$show_hidden = 0, $sticky = 1
) {
//if (! (is_numeric($forumID) && is_numeric($min) && is_numeric($nRec))) {
// return NULL; // Something is wrong here.
//}
$sql = 'forum = ' . $forumID ;
if ($sticky){
$stickysql = "sticky DESC, ";
}
if (!$show_hidden) {
$sql .= ' AND hidden = 0';
}
switch($sort_style) {
case MODIFIED_NEW:
$sql .= ' ORDER BY '.$stickysql.'timestamp DESC';
break;
case MODIFIED_OLD:
$sql .= ' ORDER BY '.$stickysql.'timestamp ASC';
break;
case VIEWS_MOST:
$sql .= ' ORDER BY '.$stickysql.'views DESC';
break;
case REPLIES_MOST:
$sql .= ' ORDER BY '.$stickysql.'replies DESC';
break;
case CREATE_TIME_NEW:
$sql .= ' ORDER by '.$stickysql.'create_time desc';
break;
case CREATE_TIME_OLD:
$sql .= ' ORDER by '.$stickysql.'create_time asc';
break;
case 'sufferers':
$sql .= ' ORDER by '.$stickysql.'sufferers desc';
break;
case 'activity':
$sql .= ' ORDER by '.$stickysql.'activity desc';
break;
case 'score':
$sql .= ' ORDER by '.$stickysql.'score desc';
break;
default:
$sql .= ' ORDER BY '.$stickysql.'timestamp DESC';
break;
}
if ($start > -1) {
$sql .= ' LIMIT '.$start;
if ($nRec > -1) {
$sql .= ', '.$nRec;
}
} else if ($nRec > -1) {
$sql .= ' LIMIT '.$nRec;
}
return BoincThread::enum($sql);
}
// $show_hidden = true when it is a moderator reading
// error_page if this function returns NULL.
// $sort_style - string (checked by switch statement)
// $show_hidden - bool (not directly passed to SQL)
//
function get_thread_posts($threadid, $sort_style, $show_hidden) {
$sql = "thread=$threadid";
if (!$show_hidden) {
$sql .= ' AND hidden = 0';
}
switch($sort_style) {
case CREATE_TIME_NEW:
$sql .= ' ORDER BY timestamp desc';
break;
case CREATE_TIME_OLD:
$sql .= ' ORDER BY timestamp asc';
break;
case POST_SCORE:
$sql .= ' ORDER BY score DESC';
break;
default:
$sql .= ' ORDER BY timestamp asc';
break;
}
return BoincPost::enum($sql);
}
// Show the links for possible moderation actions related to a single post
//
function show_post_moderation_links(
$config, $logged_in_user, $post, $forum, $tokens
){
$moderators_allowed_to_ban = parse_bool($config, "moderators_allowed_to_ban");
$moderators_vote_to_ban = parse_bool($config, "moderators_vote_to_ban");
if ($post->hidden) {
show_button("forum_moderate_post_action.php?action=unhide&id=".$post->id."$tokens", "Unhide", "Unhide this post");
} else {
show_button("forum_moderate_post.php?action=hide&id=".$post->id."$tokens", "Hide", "Hide this post");
}
show_button(
"forum_moderate_post.php?action=move&id=".$post->id."$tokens",
"Move", "Move post to a different thread"
);
if ($forum->parent_type == 0) {
if ($logged_in_user->prefs->privilege(S_ADMIN) || ($logged_in_user->prefs->privilege(S_MODERATOR) && $moderators_allowed_to_ban)) {
show_button("forum_moderate_post.php?action=banish_user&id=".$post->id."&userid=".$post->user."$tokens", "Banish author", "Banish author");
}
if ($logged_in_user->prefs->privilege(S_MODERATOR) && $moderators_vote_to_ban) {
require_once("../inc/forum_banishment_vote.inc");
if (vote_is_in_progress($post->user)) {
show_button("forum_banishment_vote.php?action=yes&userid=".$post->user, "Vote to banish author", "Vote to banish author");
show_button("forum_banishment_vote.php?action=no&userid=".$post->user, "Vote not to banish author", "Vote not to banish author");
} else {
show_button("forum_banishment_vote.php?action=start&userid=".$post->user, "Start vote to banish author", "Start vote to banish author");
}
}
}
}
function check_create_thread_access($user, $forum) {
if ($forum->is_dev_blog){
if (
(!$user->prefs->privilege(S_SCIENTIST)) &&
(!$user->prefs->privilege(S_DEV)) &&
(!$user->prefs->privilege(S_ADMIN))
) {
error_page("This forum is marked as a development blog, only people directly working with the project may start a new thread here.
However, you may post a reply to an existing thread.");
}
}
check_post_access($user, $forum);
}
function check_post_access($user, $forum) {
switch ($forum->parent_type) {
case 0:
if ($user->prefs->privilege(S_MODERATOR)) return;
break;
case 1:
$team = BoincTeam::lookup_id($forum->category);
if (is_team_admin($user, $team)) return;
// non-team-members can't post
//
if ($user->teamid != $team->id) {
error_page("Only team members can post to the team message board");
}
break;
}
// If user haven't got enough credit (according to forum regulations)
// We do not tell the (ab)user how much this is -
// no need to make it easy for them to break the system.
//
if ($user->total_credit<$forum->post_min_total_credit || $user->expavg_credit<$forum->post_min_expavg_credit) {
error_page(tra("In order to create a new thread in %1 you must have a certain amount of credit. This is to prevent and protect against abuse of the system.", $forum->title));
}
// If the user is posting faster than forum regulations allow
// Tell the user to wait a while before creating any more posts
//
if (time()-$user->last_post <$forum->min_post_interval) {
error_page(tra("You cannot create any more threads right now. Please wait a while before trying again. This delay has been enforced to protect against abuse of the system."));
}
}
function check_reply_access($user, $forum, $thread) {
if ($thread->locked && !is_moderator($user, $forum)) {
error_page(
"This thread is locked. Only forum moderators and administrators are allowed to post there."
);
}
if ($thread->hidden) {
error_page(
"Can't post to a hidden thread."
);
}
check_post_access($user, $forum);
}
function is_moderator($user, $forum) {
if (!$user) return false;
switch ($forum->parent_type) {
case 0:
if ($user->prefs->privilege(S_MODERATOR)) return true;
if ($user->prefs->privilege(S_ADMIN)) return true;
if ($user->prefs->privilege(S_DEV)) return true;
break;
case 1:
if ($user->prefs->privilege(S_ADMIN)) return true;
$team = BoincTeam::lookup_id($forum->category);
return is_team_admin($user, $team);
break;
}
return false;
}
?>