kivitendo/SL/File/Backend/Filesystem.pm @ bef56e29
8c7f25bc | Martin Helmling | package SL::File::Backend::Filesystem;
|
||
use strict;
|
||||
use parent qw(SL::File::Backend);
|
||||
use SL::DB::File;
|
||||
4412b603 | Tamino Steinert | use SL::DB::FileVersion;
|
||
46d48c74 | Jan Büren | |||
bef56e29 | Tamino Steinert | use Carp;
|
||
use List::Util qw(first);
|
||||
8c7f25bc | Martin Helmling | use File::Copy;
|
||
use File::Slurp;
|
||||
c5057972 | Bernd Bleßmann | use File::stat;
|
||
8c7f25bc | Martin Helmling | use File::Path qw(make_path);
|
||
46d48c74 | Jan Büren | use UUID::Tiny ':std';
|
||
8c7f25bc | Martin Helmling | |||
#
|
||||
# public methods
|
||||
#
|
||||
sub delete {
|
||||
my ($self, %params) = @_;
|
||||
56371b77 | Martin Helmling | die "no dbfile in backend delete" unless $params{dbfile};
|
||
bef56e29 | Tamino Steinert | my @versions = @{$params{dbfile}->file_versions_sorted};
|
||
my @versions_to_delete;
|
||||
if ($params{last}) {
|
||||
my $last = pop @versions;
|
||||
@versions_to_delete = ($last);
|
||||
} elsif ($params{all_but_notlast}) {
|
||||
pop @versions; # remove last
|
||||
@versions_to_delete = @versions;
|
||||
} elsif ($params{version}) {
|
||||
my $version = first {$_->version == $params{version}} @versions
|
||||
or confess "Version not found.";
|
||||
@versions_to_delete = ($version);
|
||||
8c7f25bc | Martin Helmling | } else {
|
||
bef56e29 | Tamino Steinert | @versions_to_delete = @versions;
|
||
8c7f25bc | Martin Helmling | }
|
||
bef56e29 | Tamino Steinert | foreach my $version (@versions_to_delete) {
|
||
unlink($version->get_system_location());
|
||||
$version->delete;
|
||||
}
|
||||
return 1;
|
||||
8c7f25bc | Martin Helmling | }
|
||
sub save {
|
||||
my ($self, %params) = @_;
|
||||
c17045ff | Jan Büren | |||
die 'dbfile not exists' unless ref $params{dbfile} eq 'SL::DB::File';
|
||||
die 'no file contents' unless $params{file_path} || $params{file_contents};
|
||||
my $dbfile = delete $params{dbfile};
|
||||
c5057972 | Bernd Bleßmann | |||
# Do not save and do not create a new version of the document if file size of last version is the same.
|
||||
if ($dbfile->source eq 'created' && $self->get_version_count(dbfile => $dbfile)) {
|
||||
my $new_file_size = $params{file_path} ? stat($params{file_path})->size : length($params{file_contents});
|
||||
my $last_file_size = stat($self->_filesystem_path($dbfile))->size;
|
||||
return 1 if $last_file_size == $new_file_size;
|
||||
}
|
||||
bef56e29 | Tamino Steinert | my @versions = @{$dbfile->file_versions_sorted};
|
||
my $new_version_number = scalar @versions ? $versions[-1]->version + 1 : 1;
|
||||
8c7f25bc | Martin Helmling | |||
bef56e29 | Tamino Steinert | my $tofile = $self->_filesystem_path($dbfile, $new_version_number);
|
||
8c7f25bc | Martin Helmling | if ($params{file_path} && -f $params{file_path}) {
|
||
File::Copy::copy($params{file_path}, $tofile);
|
||||
c17045ff | Jan Büren | } elsif ($params{file_contents}) {
|
||
8c7f25bc | Martin Helmling | open(OUT, "> " . $tofile);
|
||
print OUT $params{file_contents};
|
||||
close(OUT);
|
||||
}
|
||||
4412b603 | Tamino Steinert | |||
# save file version
|
||||
46d48c74 | Jan Büren | my $doc_path = $::lx_office_conf{paths}->{document_path};
|
||
my $rel_file = $tofile;
|
||||
$rel_file =~ s/$doc_path//;
|
||||
my $fv = SL::DB::FileVersion->new(
|
||||
4412b603 | Tamino Steinert | file_id => $dbfile->id,
|
||
bef56e29 | Tamino Steinert | version => $new_version_number,
|
||
4412b603 | Tamino Steinert | file_location => $rel_file,
|
||
doc_path => $doc_path,
|
||||
backend => 'Filesystem',
|
||||
guid => create_uuid_as_string(UUID_V4),
|
||||
)->save;
|
||||
74d8dd6f | Martin Helmling | if ($params{mtime}) {
|
||
utime($params{mtime}, $params{mtime}, $tofile);
|
||||
}
|
||||
8c7f25bc | Martin Helmling | return 1;
|
||
}
|
||||
sub get_version_count {
|
||||
my ($self, %params) = @_;
|
||||
die "no dbfile" unless $params{dbfile};
|
||||
bef56e29 | Tamino Steinert | my $file_id = $params{dbfile}->id;
|
||
return 0 unless defined $file_id;
|
||||
return SL::DB::Manager::FileVersion->get_all_count(where => [file_id => $file_id]);
|
||||
8c7f25bc | Martin Helmling | }
|
||
sub get_mtime {
|
||||
my ($self, %params) = @_;
|
||||
bef56e29 | Tamino Steinert | my $path = $self->get_filepath(%params);
|
||
c5dc4974 | Jan Büren | |||
c5057972 | Bernd Bleßmann | my $dt = DateTime->from_epoch(epoch => stat($path)->mtime, time_zone => $::locale->get_local_time_zone()->name, locale => $::lx_office_conf{system}->{language})->clone();
|
||
8c7f25bc | Martin Helmling | return $dt;
|
||
}
|
||||
sub get_filepath {
|
||||
my ($self, %params) = @_;
|
||||
die "no dbfile" unless $params{dbfile};
|
||||
my $path = $self->_filesystem_path($params{dbfile},$params{version});
|
||||
2329b45d | Bernd Bleßmann | |||
die "No file found at $path. Expected: $params{dbfile}{file_name}, file.id: $params{dbfile}{id}" if !-f $path;
|
||||
8c7f25bc | Martin Helmling | 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 enabled {
|
||||
bf980f4c | Martin Helmling | return 0 unless $::instance_conf->get_doc_files;
|
||
return 0 unless $::lx_office_conf{paths}->{document_path};
|
||||
return 0 unless -d $::lx_office_conf{paths}->{document_path};
|
||||
8c7f25bc | Martin Helmling | return 1;
|
||
}
|
||||
cff4d333 | Martin Helmling | sub sync_from_backend {
|
||
bef56e29 | Tamino Steinert | die "Not implemented";
|
||
cff4d333 | Martin Helmling | }
|
||
8c7f25bc | Martin Helmling | |||
#
|
||||
# internals
|
||||
#
|
||||
sub _filesystem_path {
|
||||
my ($self, $dbfile, $version) = @_;
|
||||
bf980f4c | Martin Helmling | die "No files backend enabled" unless $::instance_conf->get_doc_files || $::lx_office_conf{paths}->{document_path};
|
||
8c7f25bc | Martin Helmling | |||
# use filesystem with depth 3
|
||||
bef56e29 | Tamino Steinert | $version ||= $dbfile->file_versions_sorted->[-1]->version;
|
||
confess "Version is required." unless $version;
|
||||
8c7f25bc | Martin Helmling | my $iddir = sprintf("%04d", $dbfile->id % 1000);
|
||
bf980f4c | Martin Helmling | my $path = File::Spec->catdir($::lx_office_conf{paths}->{document_path}, $::auth->client->{id}, $iddir, $dbfile->id);
|
||
8c7f25bc | Martin Helmling | if (!-d $path) {
|
||
File::Path::make_path($path, { chmod => 0770 });
|
||||
}
|
||||
return File::Spec->catdir($path, $dbfile->id . '_' . $version);
|
||||
}
|
||||
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. In this Subdirectories for each file an additional subdirectory exists
|
||||
for the versions of this file.
|
||||
The Versioning is done via a Versionnumber which is incremented by one for each version.
|
||||
So the Version 2 of the file with the database id 4 is stored as path {root}/0004/4/4_2.
|
||||
=head1 METHODS
|
||||
See methods of L<SL::File::Backend>.
|
||||
=head1 SEE ALSO
|
||||
L<SL::File::Backend>
|
||||
=head1 AUTHOR
|
||||
Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
|
||||
=cut
|