#!/usr/bin/perl #------------------------------------------------------------------------------ # mwForum - Web-based discussion forum # Copyright (c) 1999-2009 Markus Wichitill # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. #------------------------------------------------------------------------------ use strict; use warnings; no warnings qw(uninitialized redefine); # Imports use MwfMain; #------------------------------------------------------------------------------ # Init my ($m, $cfg, $lng, $user, $userId) = MwfMain->new(@_); # Check if access should be denied $m->checkBan($userId) if $cfg->{banViewing}; # Get CGI parameters my $boardId = $m->paramStr('bid'); # int() later my $jumpTopicId = $m->paramInt('tid'); my $page = $m->paramInt('pg') || 1; # Redirect if board ID is special $m->redirect('forum_show', tgt => $boardId) if $boardId =~ /^cid[0-9]+\z/; $m->redirect('forum_show') if $boardId eq '0'; $boardId = int($boardId || 0); # Get boardId and stickyness from topic my $arcPfx = $m->{archive} ? 'arc_' : ''; my $jumpTopicSticky = 0; ($boardId, $jumpTopicSticky) = $m->fetchArray(" SELECT boardId, sticky FROM ${arcPfx}topics WHERE id = ?", $jumpTopicId) if $jumpTopicId; $boardId or $m->error('errBrdIdMiss'); # Get board/category my $board = $m->fetchHash(" SELECT boards.*, categories.id AS categId, categories.title AS categTitle FROM ${arcPfx}boards AS boards INNER JOIN categories AS categories ON categories.id = boards.categoryId WHERE boards.id = ?", $boardId); $board or $m->error('errBrdNotFnd'); # Check if user can see and write to board my $boardAdmin = $user->{admin} || $m->boardAdmin($userId, $board->{id}); $boardAdmin || $m->boardVisible($board) or $m->error('errNoAccess'); my $boardWritable = $boardAdmin || $m->boardWritable($board); # Print header $m->printHeader($board->{title}); # Set current page to a requested topic's page my $topicsPP = $m->min($user->{topicsPP}, $cfg->{maxTopicsPP}) || $cfg->{maxTopicsPP}; if ($jumpTopicSticky) { $page = 1 } elsif ($jumpTopicId) { my $jumpTopicTime = $m->fetchArray(" SELECT lastPostTime FROM ${arcPfx}topics WHERE id = ?", $jumpTopicId); $page = $m->fetchArray(" SELECT COUNT(*) / :topicsPP + 1 FROM ${arcPfx}topics WHERE boardId = :boardId AND (sticky = 1 OR lastPostTime > :jumpTopicTime)", { topicsPP => $topicsPP, boardId => $boardId, jumpTopicTime => $jumpTopicTime }) if $jumpTopicTime; $page = int($page); } # Get topics my $offset = ($page - 1) * $topicsPP; my $stickyStr = $cfg->{skipStickySort} ? "" : "sticky DESC,"; my $topics = $m->fetchAllHash(" SELECT topics.id, topics.subject, topics.tag, topics.pollId, topics.locked, topics.sticky, topics.postNum, topics.lastPostTime, posts.userId, posts.approved, posts.userNameBak FROM ( SELECT id, basePostId, subject, tag, pollId, locked, sticky, postNum, lastPostTime FROM ${arcPfx}topics WHERE boardId = :boardId ORDER BY $stickyStr lastPostTime DESC LIMIT :topicsPP OFFSET :offset ) AS topics INNER JOIN ${arcPfx}posts AS posts ON posts.id = topics.basePostId", { boardId => $boardId, topicsPP => $topicsPP, offset => $offset }); # Put visible topics in by-id lookup table and collect IDs for the next query @$topics = grep($_->{approved} || $userId && $userId == $_->{userId}, @$topics) if !$boardAdmin; my %topics = map(($_->{id} => $_), @$topics); my @topicIds = map($_->{id}, @$topics); # Get new post and unread numbers for topics on page my $lowestUnreadTime = $m->max($user->{fakeReadTime}, $m->{now} - $cfg->{maxUnreadDays} * 86400); my $newPostsExist = 0; my $unreadPostsExist = 0; if ($userId && !$m->{archive}) { my $approvedStr = $user->{admin} ? "" : "AND approved = 1"; my $newTopics = $m->fetchAllArray(" SELECT topicId, COUNT(*) AS newNum FROM posts WHERE topicId IN (:topicIds) AND postTime > :prevOnTime $approvedStr GROUP BY topicId", { topicIds => \@topicIds, prevOnTime => $user->{prevOnTime} }); my $unreadTopics = $m->fetchAllArray(" SELECT topics.id FROM topics AS topics LEFT JOIN topicReadTimes AS topicReadTimes ON topicReadTimes.userId = :userId AND topicReadTimes.topicId = topics.id WHERE topics.id IN (:topicIds) AND topics.lastPostTime > :lowestUnreadTime AND (topics.lastPostTime > topicReadTimes.lastReadTime OR topicReadTimes.topicId IS NULL)", { userId => $userId, topicIds => \@topicIds, lowestUnreadTime => $lowestUnreadTime }); for my $topic (@$newTopics) { $topics{$topic->[0]}{newNum} = $topic->[1]; $newPostsExist = 1; } for my $topic (@$unreadTopics) { $topics{$topic->[0]}{hasUnread} = 1; $unreadPostsExist = 1; } } # Page links my @pageLinks = (); my $topicNum = $m->fetchArray(" SELECT COUNT(*) FROM ${arcPfx}topics WHERE boardId = ?", $boardId); my $pageNum = int($topicNum / $topicsPP) + ($topicNum % $topicsPP != 0); if ($pageNum > 1) { my $maxPageNum = $m->min($pageNum, 8); push @pageLinks, { url => $m->url('board_show', bid => $boardId, pg => $_), txt => $_, dsb => $_ == $page } for 1 .. $maxPageNum; push @pageLinks, { txt => "..." } if $maxPageNum < $pageNum - 1; push @pageLinks, { url => $m->url('board_show', bid => $boardId, pg => $pageNum), txt => $pageNum, dsb => $pageNum == $page } if $maxPageNum < $pageNum; push @pageLinks, { url => $m->url('board_show', bid => $boardId, pg => $page - 1), txt => 'comPgPrev', dsb => $page == 1 }; my $url = $cfg->{seoRewrite} && !$userId ? "board_${boardId}_" . ($page + 1) . ".html" : $m->url('board_show', bid => $boardId, pg => $page + 1); push @pageLinks, { url => $url, txt => 'comPgNext', dsb => $page == $pageNum }; } # Navigation button links my @navLinks = (); push @navLinks, { url => $m->url('prevnext', bid => $boardId, dir => 'prev'), txt => 'brdPrev', ico => 'prev' }; push @navLinks, { url => $m->url('prevnext', bid => $boardId, dir => 'next'), txt => 'brdNext', ico => 'next' }; push @navLinks, { url => $m->url('forum_show', tgt => "bid$boardId"), txt =>'comUp', ico => 'up' }; # User button links my @userLinks = (); if (!$m->{archive}) { push @userLinks, { url => $m->url('board_info', bid => $boardId), txt => 'brdInfo', ico => 'info' }; push @userLinks, { url => $m->url('topic_add', bid => $boardId), txt => 'brdNewTpc', ico => 'write' } if $boardWritable; push @userLinks, { url => $m->url('board_ratings', bid => $boardId), txt => 'brdRatings', ico => 'rate' } if $cfg->{linkAvgRatPgs} && $board->{rate}; push @userLinks, { url => $m->url('forum_overview', act => 'new', bid => $boardId), txt => 'comShowNew', ico => 'shownew' } if $userId && $newPostsExist; push @userLinks, { url => $m->url('forum_overview', act => 'unread', bid => $boardId), txt => 'comShowUnr', ico => 'showunread' } if $userId && $unreadPostsExist; $m->callPlugin($cfg->{includePlg}{boardUserLink}, links => \@userLinks, board => $board); } # Admin button links my @adminLinks = (); if ($boardAdmin && !$m->{archive}) { my $reportNum = 0; $reportNum = $m->fetchArray(" SELECT COUNT(*) FROM postReports AS postReports INNER JOIN posts AS posts ON posts.id = postReports.postId INNER JOIN boards AS boards ON boards.id = posts.boardId WHERE boards.id = ?", $boardId) if $cfg->{reports}; if ($user->{admin}) { push @adminLinks, { url => $m->url('board_options', bid => $boardId, ori => 1), txt => 'Options', ico => 'option' }; push @adminLinks, { url => $m->url('board_groups', bid => $boardId, ori => 1), txt => 'Groups', ico => 'group' }; push @adminLinks, { url => $m->url('board_merge', bid => $boardId), txt => 'Merge', ico => 'merge' }; push @adminLinks, { url => $m->url('board_split', bid => $boardId), txt => 'Split', ico => 'split' }; push @adminLinks, { url => $m->url('board_archive', bid => $boardId), txt => 'Archive', ico => 'archive' } if !$m->{sqlite}; push @adminLinks, { url => $m->url('user_confirm', bid => $boardId, script => 'board_delete', name => $board->{title}), txt => 'Delete', ico => 'delete' }; push @adminLinks, { url => $m->url('report_list', bid => $boardId), txt => "Reports ($reportNum)", ico => 'report' } if $reportNum; $m->callPlugin($cfg->{includePlg}{boardAdminLink}, links => \@adminLinks, board => $board); } else { push @adminLinks, { url => $m->url('board_groups', bid => $boardId, ori => 1), txt => 'brdAdmGrp', ico => 'group' }; push @adminLinks, { url => $m->url('board_split', bid => $boardId), txt => 'brdAdmSpl', ico => 'split' }; push @adminLinks, { url => $m->url('report_list', bid => $boardId), txt => "$lng->{brdAdmRep} ($reportNum)", ico => 'report' } if $reportNum; $m->callPlugin($cfg->{includePlg}{boardAdminLink}, links => \@adminLinks, board => $board); } } # Print page bar my $url = $m->url('forum_show', tgt => "bid$boardId"); my $categStr = "$board->{categTitle} / "; my $pageStr = $page > 1 ? " ($lng->{comPgTtl} $page)" : ""; $m->printPageBar(mainTitle => $lng->{brdTitle}, subTitle => $categStr . $board->{title} . $pageStr, navLinks => \@navLinks, pageLinks => \@pageLinks, userLinks => \@userLinks, adminLinks => \@adminLinks); # Print long description print "
$lng->{brdTopic} | \n", "$lng->{brdPoster} | \n", "$lng->{brdPosts} | \n", "$lng->{brdLastPost} | \n", "
---|---|---|---|
\n", "\n", "$lockImg$invisImg$pollImg\n", "$subject$tag\n", " | \n", "$userNameStr | \n", "$topic->{postNum} $newNumStr | \n", "$lastPostTimeStr | \n", "