Files
tyforum/script/post_attach.pl
2023-10-16 19:17:15 +09:00

369 lines
13 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 redefine);
# Imports
use TyfMain;
#------------------------------------------------------------------------------
# Init
my ( $m, $cfg, $lng, $user, $userId ) = TyfMain->new( $_[0] );
# Get CGI parameters
$m->{ajax} = $m->paramBool('ajax');
my $action = $m->paramStrId('act');
my $postId = $m->paramInt('pid');
my $attachId = $m->paramInt('aid');
my $caption = $m->paramStr('caption');
my $embed = $m->paramBool('embed');
my $change = $m->paramBool('change');
my $delete = $m->paramBool('delete');
my $submitted = $m->paramBool('subm');
$postId or $m->error('errParamMiss') if $action eq 'upload';
$attachId or $m->error('errParamMiss') if $change || $delete;
# Get attachment
my $attach = undef;
if ($attachId) {
$attach = $m->fetchHash( "
SELECT * FROM attachments WHERE id = ?", $attachId );
$attach or $m->error('errAttNotFnd');
$postId = $attach->{postId};
}
# Get post
my $post = $m->fetchHash( "
SELECT * FROM posts WHERE id = ?", $postId );
$post or $m->error('errPstNotFnd');
my $postIdMod = $postId % 100;
my $boardId = $post->{boardId};
my $topicId = $post->{topicId};
# Get board
my $board = $m->fetchHash( "
SELECT * FROM boards WHERE id = ?", $boardId );
# Check if user can see and write to board
my $boardAdmin
= $user->{admin}
|| $m->boardAdmin( $userId, $boardId )
|| $board->{topicAdmins} && $m->topicAdmin( $userId, $topicId );
$boardAdmin || $m->boardVisible($board) or $m->error('errNoAccess');
$boardAdmin || $m->boardWritable( $board, 1 ) or $m->error('errNoAccess');
# Check if user owns post or is moderator
$userId && $userId == $post->{userId} || $boardAdmin
or $m->error('errNoAccess');
# Check if attachments are enabled
$cfg->{attachments}
&& ( $board->{attach} == 1 || $board->{attach} == 2 && $boardAdmin )
or $m->error('errNoAccess');
# Check if topic or post is locked
!$m->fetchArray( "
SELECT locked FROM topics WHERE id = ?", $topicId )
|| $boardAdmin
or $m->error('errTpcLocked');
!$post->{locked} || $boardAdmin or $m->error('errPstLocked');
# Check authorization
$m->checkAuthz( $user, 'attach' );
# Disable embed if not allowed
$embed = 0 if !$cfg->{attachImg};
# Process form
if ($submitted) {
# Check request source authentication
$m->checkSourceAuth() or $m->formError('errSrcAuth');
# Process upload form
if ( $action eq 'upload' ) {
# Get upload, check filename and size
my ( $upload, $fileName, $fileSize ) = $m->getUpload('file');
length($fileName) or $m->error('errAttName');
$fileSize or $m->error('errAttSize');
# Make sure filenames don't clash
my ( $name, $ext ) = $fileName =~ /(.+?)(\.[^.]+)?\z/;
$name = substr( $name, 0, $cfg->{attachNameLen} || 40 );
$ext = substr( $ext, 0, $cfg->{attachNameLen} || 40 );
my $webImage = $ext =~ /^\.(?:jpg|png|gif)\z/i ? 1 : 0;
my $like = $m->{pgsql} ? 'ILIKE' : 'LIKE';
my $num = "";
for my $i ( 0 .. 100 ) {
$num = $i ? "-$i" : "";
my $nameExists = $m->fetchArray( "
SELECT 1 FROM attachments WHERE postId = ? AND LOWER(fileName) $like LOWER(?)",
$postId, $webImage ? "$name$num%" : "$name$num$ext" );
last if !$nameExists;
$i < 100 or $m->formError("Too many filename collisions.");
}
$fileName = "$name$num$ext";
# If there's no error, finish action
if ( !@{ $m->{formErrors} } ) {
# Create directories and attachment file
my $path = "$cfg->{attachFsPath}/$postIdMod";
$m->createDirectories( $path, $postId );
$m->saveUpload( 'file', $upload,
"$path/$postId/" . $m->encFsPath($fileName) );
# Add attachments table entry
$webImage = 2 if $webImage && $embed;
$caption = substr( $caption, 0, 100 );
my $captionEsc = $m->escHtml($caption);
$m->dbDo( "
INSERT INTO attachments (postId, webImage, fileName, caption) VALUES (?, ?, ?, ?)",
$postId, $webImage, $fileName, $captionEsc );
$attachId = $m->dbInsertId("attachments");
# Resize image
$m->resizeAttachment($attachId)
if $webImage && $cfg->{attachImgRsz};
# Log action and finish
$m->logAction( 1, 'post', 'attach', $userId, $boardId, $topicId,
$postId, $attachId );
if ( $m->{ajax} ) {
$m->printHttpHeader();
print $m->json( { ok => 1 } );
}
else {
$m->redirect(
'post_attach',
pid => $postId,
msg => 'PstAttach'
);
}
}
}
# Process delete all action
elsif ( $action eq 'delAll' ) {
# If there's no error, finish action
if ( !@{ $m->{formErrors} } ) {
# Delete all attachments
my $attachments = $m->fetchAllArray( "
SELECT id FROM attachments WHERE postId = ?", $postId );
for my $attachment (@$attachments) {
$m->deleteAttachment( $attachment->[0] );
}
# Log action and finish
$m->logAction( 1, 'post', 'attdlall', $userId, $boardId, $topicId,
$postId );
$m->redirect( 'post_attach', pid => $postId, msg => 'PstDetach' );
}
}
# Process delete form action
elsif ($delete) {
# If there's no error, finish action
if ( !@{ $m->{formErrors} } ) {
# Delete attachment
$m->deleteAttachment($attachId);
# Log action and finish
$m->logAction( 1, 'post', 'detach', $userId, $boardId, $topicId,
$postId, $attachId );
$m->redirect( 'post_attach', pid => $postId, msg => 'PstDetach' );
}
}
# Process change form action
elsif ($change) {
# If there's no error, finish action
if ( !@{ $m->{formErrors} } ) {
# Update attachment
my $webImage
= $attach->{fileName} =~ /\.(?:jpg|png|gif)\z/i ? 1 : 0;
$webImage = 2 if $webImage && $embed;
$caption = substr( $caption, 0, 100 );
my $captionEsc = $m->escHtml($caption);
$m->dbDo( "
UPDATE attachments SET webImage = ?, caption = ? WHERE id = ?",
$webImage, $captionEsc, $attachId );
# Delete thumbnail
if ( $webImage && !$embed ) {
my $file
= "$cfg->{attachFsPath}/$postIdMod/$postId/$attach->{fileName}";
$file =~ s!\.(?:jpg|png|gif)\z!.thb.jpg!i;
unlink $file;
}
# Log action and finish
$m->logAction( 1, 'post', 'attchg', $userId, $boardId, $topicId,
$postId, $attachId );
$m->redirect( 'post_attach', pid => $postId, msg => 'PstAttChg' );
}
}
else { $m->error('errParamMiss') }
}
# Print AJAX errors or form
if ( $m->{ajax} && @{ $m->{formErrors} } ) {
$m->printHttpHeader();
print $m->json( { error => $m->{formErrors}[0] } );
}
elsif ( !$submitted || @{ $m->{formErrors} } ) {
# Print header
$m->printHeader( undef,
{ postId => $postId, maxAttachLen => $cfg->{maxAttachLen} } );
# Get existing attachments
my $attachments = $m->fetchAllHash( "
SELECT * FROM attachments WHERE postId = ? ORDER BY id", $postId );
# Print page bar
my @navLinks = (
{ url => $m->url( 'topic_show', pid => $postId ),
txt => 'comUp',
ico => 'up'
}
);
my @userLinks = ();
push @userLinks,
{
url => $m->url(
'user_confirm',
script => 'post_attach',
pid => $postId,
act => 'delAll'
),
txt => 'attDelAll',
ico => 'delete'
}
if @$attachments;
$m->printPageBar(
mainTitle => $lng->{attTitle},
navLinks => \@navLinks,
userLinks => \@userLinks
);
# Print hints and form errors
if ( $m->paramDefined('msg') ) { $m->printHints( ['attGoPostT'] ) }
else { $m->printHints( [ $lng->{attDropNote} ], 'dropNote', 1 ) }
$m->printFormErrors();
# Prepare values
my $sizeStr = $m->formatSize( $cfg->{maxAttachLen} );
my $label = $m->formatStr( $lng->{attUplFiles}, { size => $sizeStr } );
my $embedChk = $cfg->{attachImgDef} ? 'checked' : "";
# Print attachment form
print
"<form id=\"upload\" action=\"post_attach$m->{ext}\" method=\"POST\" enctype=\"multipart/form-data\">\n",
"<div class=\"frm\">\n",
"<div class=\"hcl\"><span class=\"htt\">$lng->{attUplTtl}</span></div>\n",
"<div class=\"ccl\" id=\"dropZone\">\n",
"<fieldset>\n",
"<label class=\"lbw\">$label\n",
"<input type=\"file\" name=\"file\" autofocus></label>\n",
"<label class=\"lbw\">$lng->{attUplCapt}\n",
"<input type=\"text\" class=\"hwi\" name=\"caption\" maxlength=\"100\"></label>\n",
"</fieldset>\n";
print
"<fieldset>\n",
"<label><input type=\"checkbox\" name=\"embed\" $embedChk>$lng->{attUplEmbed}</label>\n",
"</fieldset>\n"
if $cfg->{attachImg};
print
$m->submitButton( 'attUplB', 'attach', 'upload' ),
"<input type=\"hidden\" name=\"pid\" value=\"$postId\">\n",
"<input type=\"hidden\" name=\"act\" value=\"upload\">\n",
$m->stdFormFields(),
"</div>\n",
"</div>\n",
"</form>\n\n";
# Print existing attachments
for my $attach (@$attachments) {
my $fileName = $attach->{fileName};
print
"<form action=\"post_attach$m->{ext}\" method=\"POST\">\n",
"<div class=\"frm\">\n",
"<div class=\"hcl\"><span class=\"htt\">$lng->{attAttTtl}</span> $fileName</div>\n",
"<div class=\"ccl\">\n";
my $attFile = "$cfg->{attachFsPath}/$postIdMod/$postId/$fileName";
my $attUrl = "$cfg->{attachUrlPath}/$postIdMod/$postId/$fileName";
my $imgShowUrl = $m->url( 'attach_show', aid => $attach->{id} );
my $caption = $attach->{caption};
my $sizeStr = $m->formatSize( -s $m->encFsPath($attFile) );
$embedChk = $attach->{webImage} == 2 ? 'checked' : "";
if ( $cfg->{attachImg}
&& $attach->{webImage} == 2
&& $user->{showImages} )
{
my $thbFile = $attFile;
my $thbUrl = $attUrl;
$thbFile =~ s!\.(?:jpg|png|gif)\z!.thb.jpg!i;
$thbUrl =~ s!\.(?:jpg|png|gif)\z!.thb.jpg!i;
my $title = "title=\"$sizeStr\"";
print $cfg->{attachImgThb}
&& ( -f $m->encFsPath($thbFile)
|| $m->addThumbnail($attFile) )
? "<p><a href=\"$imgShowUrl\"><img class=\"amt\" src=\"$thbUrl\" $title alt=\"\"></a></p>"
: "<p><img class=\"ami\" src=\"$attUrl\" $title alt=\"\"></p>";
}
elsif ( $attach->{webImage} ) {
print "<p><a href=\"$imgShowUrl\">$fileName</a> ($sizeStr)</p>";
}
else {
print "<p><a href=\"$attUrl\">$fileName</a> ($sizeStr)</p>";
}
print
"<label class=\"lbw\">$lng->{attUplCapt}\n",
"<input type=\"text\" class=\"hwi\" name=\"caption\" maxlength=\"100\" value=\"$caption\"></label>\n",
$cfg->{attachImg} && $attach->{webImage}
? "<div><label><input type=\"checkbox\" name=\"embed\" $embedChk>$lng->{attUplEmbed}</label></div>\n"
: "",
$m->submitButton( 'attAttChgB', 'edit', 'change' ),
$m->submitButton( 'attAttDelB', 'delete', 'delete' ),
"<input type=\"hidden\" name=\"aid\" value=\"$attach->{id}\">\n",
$m->stdFormFields(),
"</div>\n",
"</div>\n",
"</form>\n\n";
}
# Log action and finish
$m->logAction( 3, 'post', 'attach', $userId, $boardId, $topicId,
$postId );
$m->printFooter();
}
$m->finish();