forked from github/Quit.mwForum
306 lines
9.9 KiB
Perl
Executable File
306 lines
9.9 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
#------------------------------------------------------------------------------
|
|
# mwForum - Web-based discussion forum
|
|
# Copyright (c) 1999-2015 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);
|
|
|
|
# Imports
|
|
use Getopt::Std ();
|
|
use POSIX ();
|
|
use TyfMain;
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
# Get arguments
|
|
my %opts = ();
|
|
Getopt::Std::getopts( 'sf:', \%opts );
|
|
my $spawned = $opts{s};
|
|
my $forumId = $opts{f};
|
|
|
|
# Init
|
|
my ( $m, $cfg, $lng )
|
|
= TyfMain->newShell( forumId => $forumId, spawned => $spawned );
|
|
|
|
# Create xml directory
|
|
my $feedFsPath = "$cfg->{attachFsPath}/xml";
|
|
$m->createDirectories($feedFsPath);
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Generate Atom/RSS feeds
|
|
|
|
# Generate separate files for boards
|
|
my $boards = $m->fetchAllHash( "
|
|
SELECT id, title FROM boards WHERE private = 0" );
|
|
|
|
for my $board (@$boards) {
|
|
my $boardId = $board->{id};
|
|
my $rss200File = "$feedFsPath/board$boardId.rss200.xml";
|
|
my $atom10File = "$feedFsPath/board$boardId.atom10.xml";
|
|
my $rss200Time = ( stat($rss200File) )[9];
|
|
my $atom10Time = ( stat($atom10File) )[9];
|
|
|
|
# Get time of latest post
|
|
my $lastPostTime = $m->fetchArray( "
|
|
SELECT MAX(postTime)
|
|
FROM posts
|
|
WHERE boardId = :boardId
|
|
AND userId > -2
|
|
AND approved = 1",
|
|
{ boardId => $boardId } );
|
|
|
|
# Get latest edit time of posts
|
|
my $lastEditTime = $m->fetchArray( "
|
|
SELECT MAX(editTime)
|
|
FROM posts
|
|
WHERE boardId = :boardId
|
|
AND userId > -2
|
|
AND approved = 1",
|
|
{ boardId => $boardId } );
|
|
my $updateTime
|
|
= $lastEditTime > $lastPostTime ? $lastEditTime : $lastPostTime;
|
|
|
|
# Get latest posts
|
|
my $posts = $m->fetchAllHash( "
|
|
SELECT posts.*,
|
|
topics.subject, topics.postNum,
|
|
boards.title AS boardTitle
|
|
FROM posts AS posts
|
|
INNER JOIN topics AS topics
|
|
ON topics.id = posts.topicId
|
|
INNER JOIN boards AS boards
|
|
ON boards.id = posts.boardId
|
|
WHERE posts.boardId = :boardId
|
|
AND posts.userId > -2
|
|
AND posts.approved = 1
|
|
ORDER BY posts.postTime DESC
|
|
LIMIT :rssItems",
|
|
{ boardId => $boardId, rssItems => $cfg->{rssItems} } );
|
|
|
|
# Write file
|
|
writeRss200( $rss200File, $posts, $board->{title} )
|
|
if $updateTime > $rss200Time || !-f $rss200File;
|
|
writeAtom10( $atom10File, $posts, $board->{title} )
|
|
if $updateTime > $atom10Time || !-f $atom10File;
|
|
}
|
|
|
|
# Generate single file for whole forum
|
|
my $rss200File = "$feedFsPath/forum.rss200.xml";
|
|
my $atom10File = "$feedFsPath/forum.atom10.xml";
|
|
my $rss200Time = ( stat($rss200File) )[9];
|
|
my $atom10Time = ( stat($atom10File) )[9];
|
|
|
|
# Get time of latest post
|
|
my @exclBoardIds = $cfg->{rssExclude} =~ /(\d+)/g || (0);
|
|
my $lastPostTime = $m->fetchArray( "
|
|
SELECT MAX(posts.postTime)
|
|
FROM posts AS posts
|
|
INNER JOIN boards AS boards
|
|
ON boards.id = posts.boardId
|
|
WHERE posts.userId > -2
|
|
AND posts.approved = 1
|
|
AND boards.private = 0
|
|
AND boards.id NOT IN (:exclBoardIds)",
|
|
{ exclBoardIds => \@exclBoardIds } );
|
|
|
|
# Get latest edit time of posts
|
|
my $lastEditTime = $m->fetchArray( "
|
|
SELECT MAX(posts.editTime)
|
|
FROM posts AS posts
|
|
INNER JOIN boards AS boards
|
|
ON boards.id = posts.boardId
|
|
WHERE posts.userId > -2
|
|
AND posts.approved = 1
|
|
AND boards.private = 0
|
|
AND boards.id NOT IN (:exclBoardIds)",
|
|
{ exclBoardIds => \@exclBoardIds } );
|
|
my $updateTime
|
|
= $lastEditTime > $lastPostTime ? $lastEditTime : $lastPostTime;
|
|
|
|
# Get latest posts
|
|
my $posts = $m->fetchAllHash( "
|
|
SELECT posts.*,
|
|
topics.subject, topics.postNum,
|
|
boards.title AS boardTitle
|
|
FROM posts AS posts
|
|
INNER JOIN topics AS topics
|
|
ON topics.id = posts.topicId
|
|
INNER JOIN boards AS boards
|
|
ON boards.id = posts.boardId
|
|
WHERE posts.userId > -2
|
|
AND posts.approved = 1
|
|
AND boards.private = 0
|
|
AND boards.id NOT IN (:exclBoardIds)
|
|
ORDER BY posts.postTime DESC
|
|
LIMIT :rssItems",
|
|
{ exclBoardIds => \@exclBoardIds, rssItems => $cfg->{rssItems} } );
|
|
|
|
# Write file
|
|
writeRss200( $rss200File, $posts )
|
|
if $updateTime > $rss200Time || !-f $rss200File;
|
|
writeAtom10( $atom10File, $posts )
|
|
if $updateTime > $atom10Time || !-f $atom10File;
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Write RSS 2.0 feed
|
|
|
|
sub writeRss200 {
|
|
my $file = shift();
|
|
my $posts = shift();
|
|
my $boardTitle = shift();
|
|
|
|
# Open file
|
|
open my $fh, ">:utf8", $file
|
|
or $m->error("Opening feed file failed. ($!)");
|
|
|
|
# Set locale for stupid date format
|
|
my $oldLocale = POSIX::setlocale( POSIX::LC_TIME(), 'C' );
|
|
|
|
# Format values
|
|
my $title = $cfg->{forumName};
|
|
$title .= " - $boardTitle" if $boardTitle;
|
|
my $buildDate
|
|
= $m->formatTime( $m->{now}, 0, "%a, %d %b %Y %H:%M:%S GMT" );
|
|
my $descEsc = $m->escHtml( $cfg->{rssDesc} );
|
|
|
|
# Print header
|
|
print $fh
|
|
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
|
|
"<rss version=\"2.0\">\n",
|
|
" <channel>\n",
|
|
" <title>$title</title>\n",
|
|
" <link>$cfg->{baseUrl}$cfg->{scriptUrlPath}/forum$m->{ext}</link>\n",
|
|
" <description>$descEsc</description>\n",
|
|
" <lastBuildDate>$buildDate</lastBuildDate>\n",
|
|
" <ttl>120</ttl>\n",
|
|
" <generator>tyForum $TyfMain::VERSION</generator>\n";
|
|
|
|
# Print items
|
|
my $itemLink
|
|
= "$cfg->{baseUrl}$cfg->{scriptUrlPath}/topic_show$m->{ext}?pid=";
|
|
for my $post (@$posts) {
|
|
|
|
# Format values
|
|
my $postId = $post->{id};
|
|
my $subject = $post->{subject};
|
|
$subject =~ s!'!'!g;
|
|
$subject =~ s!"!"!g;
|
|
$subject =~ s!<!!g;
|
|
$subject =~ s!>!!g;
|
|
my $postCopy = { body => $post->{body} };
|
|
$m->dbToDisplay( {}, $postCopy );
|
|
my $pubDate = $m->formatTime( $post->{postTime}, 0,
|
|
"%a, %d %b %Y %H:%M:%S GMT" );
|
|
|
|
# Print entry
|
|
print $fh
|
|
" <item>\n",
|
|
" <guid isPermaLink=\"false\">$itemLink$postId</guid>\n",
|
|
" <link>$itemLink$postId</link>\n",
|
|
" <title>$subject</title>\n",
|
|
" <author>$post->{userNameBak}</author>\n",
|
|
" <pubDate>$pubDate</pubDate>\n",
|
|
" <category>$post->{boardTitle}</category>\n",
|
|
" <description>\n",
|
|
" <![CDATA[$postCopy->{body}]]>\n",
|
|
" </description>\n",
|
|
" </item>\n";
|
|
}
|
|
|
|
# End file
|
|
print $fh
|
|
" </channel>\n",
|
|
"</rss>\n";
|
|
|
|
close $fh;
|
|
$m->setMode( $file, 'file' );
|
|
POSIX::setlocale( POSIX::LC_TIME(), $oldLocale );
|
|
}
|
|
|
|
#------------------------------------------------------------------------------
|
|
# Write Atom 1.0 feed
|
|
|
|
sub writeAtom10 {
|
|
my $file = shift();
|
|
my $posts = shift();
|
|
my $boardTitle = shift();
|
|
|
|
# Open file
|
|
open my $fh, ">:utf8", $file
|
|
or $m->error("Opening feed file failed. ($!)");
|
|
|
|
# Format values
|
|
my $title = $cfg->{forumName};
|
|
$title .= " - $boardTitle" if $boardTitle;
|
|
my $updated = $m->formatTime( $m->{now}, 0, "%Y-%m-%dT%TZ" );
|
|
my $fileName = $file;
|
|
$fileName =~ s!.*[\\/:]!!;
|
|
my $descEsc = $m->escHtml( $cfg->{rssDesc} );
|
|
|
|
# Print header
|
|
print $fh
|
|
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
|
|
"<feed xmlns=\"http://www.w3.org/2005/Atom\"",
|
|
" xmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"",
|
|
" xml:base=\"$cfg->{baseUrl}\">\n",
|
|
" <id>$cfg->{baseUrl}$cfg->{scriptUrlPath}/forum$m->{ext}</id>\n",
|
|
" <link rel=\"self\" href=\"$cfg->{baseUrl}$cfg->{attachUrlPath}/xml/$fileName\"/>\n",
|
|
" <link rel=\"alternate\" href=\"$cfg->{baseUrl}$cfg->{scriptUrlPath}/forum$m->{ext}\"/>\n",
|
|
" <title>$title</title>\n",
|
|
" <subtitle>$descEsc</subtitle>\n",
|
|
" <updated>$updated</updated>\n",
|
|
" <generator version=\"$TyfMain::VERSION\" uri=\"https://tyatsumi.gitlab.io/tyforum/\">tyForum</generator>\n",
|
|
;
|
|
|
|
# Print entries
|
|
my $itemLink
|
|
= "$cfg->{baseUrl}$cfg->{scriptUrlPath}/topic_show$m->{ext}?pid=";
|
|
my $authorLink
|
|
= "$cfg->{baseUrl}$cfg->{scriptUrlPath}/user_info$m->{ext}?uid=";
|
|
for my $post (@$posts) {
|
|
|
|
# Format values
|
|
my $postId = $post->{id};
|
|
my $postCopy = { body => $post->{body} };
|
|
$m->dbToDisplay( {}, $postCopy );
|
|
$post->{editTime} ||= $post->{postTime};
|
|
my $published
|
|
= $m->formatTime( $post->{postTime}, 0, "%Y-%m-%dT%TZ" );
|
|
my $updated = $m->formatTime( $post->{editTime}, 0, "%Y-%m-%dT%TZ" );
|
|
my $comments = $post->{postNum} - 1;
|
|
|
|
# Print entry
|
|
print $fh
|
|
" <entry>\n",
|
|
" <id>$itemLink$postId</id>\n",
|
|
" <link href=\"$itemLink$postId\"/>\n",
|
|
" <title>$post->{subject}</title>\n",
|
|
" <author><name>$post->{userNameBak}</name></author>\n",
|
|
" <published>$published</published>\n",
|
|
" <updated>$updated</updated>\n",
|
|
" <category term=\"$post->{boardTitle}\"/>\n",
|
|
" <slash:comments>$comments</slash:comments>\n",
|
|
" <content type=\"html\">\n",
|
|
" <![CDATA[$postCopy->{body}]]>\n",
|
|
" </content>\n",
|
|
" </entry>\n";
|
|
}
|
|
|
|
print $fh "</feed>\n";
|
|
close $fh;
|
|
$m->setMode( $file, 'file' );
|
|
}
|