/usr/local/CPAN/Foorum/Foorum/ResultSet/Upload.pm
package Foorum::ResultSet::Upload;
use strict;
use warnings;
our $VERSION = '1.001000';
use base 'DBIx::Class::ResultSet';
use File::Remove qw(remove);
use File::Path;
use File::Spec;
use File::Copy ();
use Foorum::Utils qw/generate_random_word/;
use Scalar::Util ();
sub get {
my ( $self, $upload_id ) = @_;
return unless ( $upload_id =~ /^\d+$/ );
my $schema = $self->result_source->schema;
my $cache = $schema->cache();
my $cache_key = "upload|upload_id=$upload_id";
my $cache_val = $cache->get($cache_key);
return $cache_val if ($cache_val);
my $upload = $self->find( { upload_id => $upload_id } );
return unless ($upload);
$cache_val = $upload->{_column_data};
$cache->set( $cache_key, $cache_val, 7200 ); # two hours
return $cache_val;
}
sub remove_for_forum {
my ( $self, $forum_id ) = @_;
my $rs = $self->search( { forum_id => $forum_id },
{ columns => [ 'upload_id', 'filename' ], } );
while ( my $u = $rs->next ) {
$self->remove_by_upload($u);
}
return 1;
}
sub remove_for_user {
my ( $self, $user_id ) = @_;
my $rs = $self->search( { user_id => $user_id, },
{ columns => [ 'upload_id', 'filename' ], } );
while ( my $u = $rs->next ) {
$self->remove_by_upload($u);
}
return 1;
}
sub remove_file_by_upload_id {
my ( $self, $upload_id ) = @_;
my $upload = $self->get($upload_id);
return unless ($upload);
remove_by_upload( $self, $upload );
return 1;
}
sub remove_by_upload {
my ( $self, $upload ) = @_;
if ( Scalar::Util::blessed($upload) ) {
$upload = $upload->{_column_data};
}
my $schema = $self->result_source->schema;
my $cache = $schema->cache();
my $base_path = $schema->base_path();
my $directory_1 = int( $upload->{upload_id} / 3200 / 3200 );
my $directory_2 = int( $upload->{upload_id} / 3200 );
my $file
= File::Spec->catfile( $base_path, 'root', 'upload', $directory_1,
$directory_2, $upload->{filename} );
remove($file);
$self->search( { upload_id => $upload->{upload_id} } )->delete;
$cache->remove( 'upload|upload_id=' . $upload->{upload_id} );
}
sub add_file {
my ( $self, $upload, $info ) = @_;
my $schema = $self->result_source->schema;
my $config = $schema->config();
my $base_path = $schema->base_path();
my @valid_types = @{ $config->{upload}->{valid_types} };
my $max_size = $config->{upload}->{max_size};
my ( $basename, $filesize ) = ( $upload->basename, $upload->size );
$filesize /= 1024; # I want K
if ( $filesize > $max_size ) {
return 'EXCEED_MAX_SIZE';
}
($filesize) = ( $filesize =~ /^(\d+\.?\d{0,1})/ ); # float(6,1)
my ( $filename_no_postfix, $filetype )
= ( $basename =~ /^(.*?)\.(\w+)$/ );
$filetype = lc($filetype);
unless ( grep { $filetype eq $_ } @valid_types ) {
return 'UNSUPPORTED_FILETYPE';
}
if ( length($filename_no_postfix) > 30 ) {
$filename_no_postfix
= substr( $filename_no_postfix, 0, 30 ); # varchar(36)
$basename = $filename_no_postfix . ".$filetype";
}
my $upload_rs = $self->create(
{ user_id => $info->{user_id},
forum_id => $info->{forum_id} || 0,
filename => $basename,
filesize => $filesize,
filetype => $filetype,
}
);
my $upload_id = $upload_rs->upload_id;
my $directory_1 = int( $upload_id / 3200 / 3200 );
my $directory_2 = int( $upload_id / 3200 );
my $upload_dir
= File::Spec->catdir( $base_path, 'root', 'upload', $directory_1,
$directory_2 );
unless ( -e $upload_dir ) {
my @created = mkpath( [$upload_dir], 0, 0777 ); ## no critic
## no critic (ProhibitLeadingZeros)
# copy index.html to protect dir from Options Indexes
my $indexfile = File::Spec->catfile( $base_path, 'root', 'upload',
'index.html' );
foreach my $dir (@created) {
File::Copy::copy( $indexfile, $dir );
}
}
my $target
= File::Spec->catfile( $base_path, 'root', 'upload', $directory_1,
$directory_2, $basename );
# rename if exist
if ( -e $target ) {
my $random_filename;
while ( -e $target ) {
$random_filename = generate_random_word(15) . ".$filetype";
$target = File::Spec->catfile( $base_path, 'root', 'upload',
$directory_1, $directory_2, $random_filename );
}
$upload_rs->update( { filename => $random_filename } );
}
unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
return 'SYSTEM_ERROR';
}
return $upload_id;
}
sub change_for_forum {
my ( $self, $info ) = @_;
my $from_id = $info->{form_id} or return 0;
my $to_id = $info->{to_id} or return 0;
$self->search( { forum_id => $from_id, } )
->update( { forum_id => $to_id, } );
}
1;