kivitendo/SL/File/Backend/Webdav.pm @ 1228b36f
0e9f27e4 | Martin Helmling | package SL::File::Backend::Webdav;
|
|
use strict;
|
|||
use parent qw(SL::File::Backend);
|
|||
use SL::DB::File;
|
|||
#use SL::Webdav;
|
|||
use File::Copy;
|
|||
use File::Slurp;
|
|||
use File::Basename;
|
|||
use File::Path qw(make_path);
|
|||
use File::MimeInfo::Magic;
|
|||
#
|
|||
# public methods
|
|||
#
|
|||
sub delete {
|
|||
my ($self, %params) = @_;
|
|||
$main::lxdebug->message(LXDebug->DEBUG2(), "del in backend " . $self . " file " . $params{dbfile});
|
|||
$main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $params{dbfile}->id * 1);
|
|||
return 0 unless $params{dbfile};
|
|||
my ($file_path, undef, undef) = $self->webdav_path($params{dbfile});
|
|||
unlink($file_path);
|
|||
return 1;
|
|||
}
|
|||
sub rename {
|
|||
my ($self, %params) = @_;
|
|||
return 0 unless $params{dbfile};
|
|||
my (undef, $oldwebdavname) = split(/:/, $params{dbfile}->location, 2);
|
|||
my ($tofile, $basepath, $basename) = $self->webdav_path($params{dbfile});
|
|||
my $fromfile = File::Spec->catfile($basepath, $oldwebdavname);
|
|||
$main::lxdebug->message(LXDebug->DEBUG2(), "renamefrom=" . $fromfile . " to=" . $tofile);
|
|||
move($fromfile, $tofile);
|
|||
}
|
|||
sub save {
|
|||
my ($self, %params) = @_;
|
|||
die 'dbfile not exists' unless $params{dbfile};
|
|||
$main::lxdebug->message(LXDebug->DEBUG2(), "in backend " . $self . " file " . $params{dbfile});
|
|||
$main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $params{dbfile}->id);
|
|||
my $dbfile = $params{dbfile};
|
|||
die 'no file contents' unless $params{file_path} || $params{file_contents};
|
|||
if ($params{dbfile}->id * 1 == 0) {
|
|||
# new element: need id for file
|
|||
$params{dbfile}->save;
|
|||
}
|
|||
my ($tofile, undef, $basename) = $self->webdav_path($params{dbfile});
|
|||
if ($params{file_path} && -f $params{file_path}) {
|
|||
copy($params{file_path}, $tofile);
|
|||
}
|
|||
elsif ($params{file_contents}) {
|
|||
open(OUT, "> " . $tofile);
|
|||
print OUT $params{file_contents};
|
|||
close(OUT);
|
|||
}
|
|||
return 1;
|
|||
}
|
|||
sub get_version_count {
|
|||
my ($self, %params) = @_;
|
|||
die "no dbfile" unless $params{dbfile};
|
|||
## TODO
|
|||
return 1;
|
|||
}
|
|||
sub get_mtime {
|
|||
my ($self, %params) = @_;
|
|||
die "no dbfile" unless $params{dbfile};
|
|||
$main::lxdebug->message(LXDebug->DEBUG2(), "version=" .$params{version});
|
|||
my ($path, undef, undef) = $self->webdav_path($params{dbfile});
|
|||
die "no file found in backend" if !-f $path;
|
|||
my @st = stat($path);
|
|||
my $dt = DateTime->from_epoch(epoch => $st[9])->clone();
|
|||
$main::lxdebug->message(LXDebug->DEBUG2(), "dt=" .$dt);
|
|||
return $dt;
|
|||
}
|
|||
sub get_filepath {
|
|||
my ($self, %params) = @_;
|
|||
die "no dbfile" unless $params{dbfile};
|
|||
my ($path, undef, undef) = $self->webdav_path($params{dbfile});
|
|||
die "no file" if !-f $path;
|
|||
return $path;
|
|||
}
|
|||
sub get_content {
|
|||
my ($self, %params) = @_;
|
|||
my $path = $self->get_filepath(%params);
|
|||
return "" unless $path;
|
|||
my $contents = File::Slurp::read_file($path);
|
|||
return \$contents;
|
|||
}
|
|||
sub sync_from_backend {
|
|||
my ($self, %params) = @_;
|
|||
return unless $params{file_type};
|
|||
$self->sync_all_locations(%params);
|
|||
}
|
|||
sub enabled {
|
|||
return 0 unless $::instance_conf->get_doc_webdav;
|
|||
return 1;
|
|||
}
|
|||
#
|
|||
# internals
|
|||
#
|
|||
my %type_to_path = (
|
|||
sales_quotation => 'angebote',
|
|||
sales_order => 'bestellungen',
|
|||
request_quotation => 'anfragen',
|
|||
purchase_order => 'lieferantenbestellungen',
|
|||
sales_delivery_order => 'verkaufslieferscheine',
|
|||
purchase_delivery_order => 'einkaufslieferscheine',
|
|||
credit_note => 'gutschriften',
|
|||
invoice => 'rechnungen',
|
|||
purchase_invoice => 'einkaufsrechnungen',
|
|||
part => 'waren',
|
|||
service => 'dienstleistungen',
|
|||
assembly => 'erzeugnisse',
|
|||
letter => 'briefe',
|
|||
general_ledger => 'dialogbuchungen',
|
|||
accounts_payable => 'kreditorenbuchungen',
|
|||
);
|
|||
my %type_to_model = (
|
|||
sales_quotation => 'Order',
|
|||
sales_order => 'Order',
|
|||
request_quotation => 'Order',
|
|||
purchase_order => 'Order',
|
|||
sales_delivery_order => 'DeliveryOrder',
|
|||
purchase_delivery_order => 'DeliveryOrder',
|
|||
credit_note => 'Invoice',
|
|||
invoice => 'Invoice',
|
|||
purchase_invoice => 'PurchaseInvoice',
|
|||
part => 'Part',
|
|||
service => 'Part',
|
|||
assembly => 'Part',
|
|||
letter => 'Letter',
|
|||
general_ledger => 'GLTransaction',
|
|||
accounts_payable => 'GLTransaction',
|
|||
);
|
|||
my %model_to_number = (
|
|||
Order => 'ordnumber',
|
|||
DeliveryOrder => 'ordnumber',
|
|||
Invoice => 'invnumber',
|
|||
PurchaseInvoice => 'invnumber',
|
|||
Part => 'partnumber',
|
|||
Letter => 'letternumber',
|
|||
GLTransaction => 'reference'
|
|||
);
|
|||
sub webdav_path {
|
|||
my ($self, $dbfile) = @_;
|
|||
#die "No webdav backend enabled" unless $::instance_conf->get_webdav;
|
|||
my $type = $type_to_path{ $dbfile->object_type };
|
|||
die "Unknown type" unless $type;
|
|||
my $number = $dbfile->backend_data;
|
|||
if ($number eq '') {
|
|||
$number = $self->_get_number_from_model($dbfile);
|
|||
$dbfile->backend_data($number);
|
|||
$dbfile->save;
|
|||
}
|
|||
$main::lxdebug->message(LXDebug->DEBUG2(), "file_name=" . $dbfile->file_name ." number=".$number);
|
|||
my @fileparts = split(/_/, $dbfile->file_name);
|
|||
my $number_ext = pop @fileparts;
|
|||
my ($maynumber, $ext) = split(/\./, $number_ext, 2);
|
|||
push @fileparts, $maynumber if $maynumber ne $number;
|
|||
my $basename = join('_', @fileparts);
|
|||
my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id}, $type, $number);
|
|||
if (!-d $path) {
|
|||
File::Path::make_path($path, { chmod => 0770 });
|
|||
}
|
|||
my $fname = $basename . '_' . $number . '_' . $dbfile->itime->strftime('%Y%m%d_%H%M%S');
|
|||
$fname .= '.' . $ext if $ext;
|
|||
$main::lxdebug->message(LXDebug->DEBUG2(), "webdav path=" . $path . " filename=" . $fname);
|
|||
return (File::Spec->catfile($path, $fname), $path, $fname);
|
|||
}
|
|||
sub get_rootdir {
|
|||
my ($self) = @_;
|
|||
#TODO immer noch das alte Problem:
|
|||
#je nachdem von woher der Aufruf kommt ist man in ./users oder .
|
|||
my $rootdir = POSIX::getcwd();
|
|||
my $basename = basename($rootdir);
|
|||
my $dirname = dirname($rootdir);
|
|||
$rootdir = $dirname if $basename eq 'users';
|
|||
return $rootdir;
|
|||
}
|
|||
sub _get_number_from_model {
|
|||
my ($self, $dbfile) = @_;
|
|||
my $class = 'SL::DB::' . $type_to_model{ $dbfile->object_type };
|
|||
eval "require $class";
|
|||
my $obj = $class->new(id => $dbfile->object_id)->load;
|
|||
die 'no object found' unless $obj;
|
|||
my $numberattr = $model_to_number{ $type_to_model{ $dbfile->object_type } };
|
|||
return $obj->$numberattr;
|
|||
}
|
|||
#
|
|||
# TODO not fully imlemented and tested
|
|||
#
|
|||
sub sync_all_locations {
|
|||
my ($self, %params) = @_;
|
|||
my %dateparms = (dateformat => 'yyyymmdd');
|
|||
foreach my $type (keys %type_to_path) {
|
|||
my @query = (
|
|||
file_type => $params{file_type},
|
|||
object_type => $type
|
|||
);
|
|||
my @oldfiles = @{ SL::DB::Manager::File->get_all(
|
|||
query => [
|
|||
file_type => $params{file_type},
|
|||
object_type => $type
|
|||
]
|
|||
)
|
|||
};
|
|||
my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id},$type_to_path{$type});
|
|||
if (opendir my $dir, $path) {
|
|||
foreach my $file (sort { lc $a cmp lc $b }
|
|||
map { decode("UTF-8", $_) } readdir $dir)
|
|||
{
|
|||
next if (($file eq '.') || ($file eq '..'));
|
|||
my $fname = $file;
|
|||
$fname =~ s|.*/||;
|
|||
my ($filename, $number, $date, $time_ext) = split(/_/, $fname);
|
|||
my ($time, $ext) = split(/\./, $time_ext, 2);
|
|||
$time = substr($time, 0, 2) . ':' . substr($time, 2, 2) . ':' . substr($time, 4, 2);
|
|||
#my @found = grep { $_->backend_data eq $fname } @oldfiles;
|
|||
#if (scalar(@found) > 0) {
|
|||
# @oldfiles = grep { $_ != @found[0] } @oldfiles;
|
|||
#}
|
|||
#else {
|
|||
my $dbfile = SL::DB::File->new();
|
|||
my $class = 'SL::DB::Manager::' . $type_to_model{$type};
|
|||
my $obj =
|
|||
$class->find_by(
|
|||
$model_to_number{ $type_to_model{$type} } => $number);
|
|||
if ($obj) {
|
|||
my $mime_type = File::MimeInfo::Magic::magic(File::Spec->catfile($path, $fname));
|
|||
if (!$mime_type) {
|
|||
# if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix
|
|||
$mime_type = File::MimeInfo::Magic::mimetype($fname);
|
|||
$mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
|
|||
}
|
|||
$dbfile->assign_attributes(
|
|||
object_id => $obj->id,
|
|||
object_type => $type,
|
|||
source => $params{file_type} eq 'document' ? 'created' : 'uploaded',
|
|||
file_type => $params{file_type},
|
|||
file_name => $filename . '_' . $number . '_' . $ext,
|
|||
mime_type => $mime_type,
|
|||
itime => $::locale->parse_date_to_object($date . ' ' . $time, %dateparms),
|
|||
);
|
|||
$dbfile->save;
|
|||
}
|
|||
#}
|
|||
closedir $dir;
|
|||
}
|
|||
}
|
|||
}
|
|||
}
|
|||
1;
|
|||
__END__
|
|||
=pod
|
|||
=encoding utf8
|
|||
=head1 NAME
|
|||
SL::File::Backend::Filesystem - Filesystem class for file storage backend
|
|||
=head1 SYNOPSIS
|
|||
See the synopsis of L<SL::File::Backend>.
|
|||
=head1 OVERVIEW
|
|||
This specific storage backend use a Filesystem which is only accessed by this interface.
|
|||
This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
|
|||
This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
|
|||
to store the files not to flat in the filesystem.
|
|||
=head1 METHODS
|
|||
See methods of L<SL::File::Backend>.
|
|||
=head1 SEE ALSO
|
|||
L<SL::File::Backend>
|
|||
=head1 TODO
|
|||
The synchronization must be tested and a periodical task is needed to synchronize in some time periods.
|
|||
=head1 AUTHOR
|
|||
Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
|
|||
=cut
|
|||