1 package SL::File::Backend::Filesystem;
5 use parent qw(SL::File::Backend);
7 use SL::DB::FileVersion;
10 use List::Util qw(first);
15 use File::Path qw(make_path);
16 use UUID::Tiny ':std';
23 my ($self, %params) = @_;
24 die "no dbfile in backend delete" unless $params{dbfile};
26 my @versions_to_delete;
27 if ($params{file_version}) {
28 croak "file_version has to be of type SL::DB::FileVersion"
29 unless ref $params{file_version} eq 'SL::DB::FileVersion';
30 @versions_to_delete = ($params{file_version});
32 my @versions = @{$params{dbfile}->file_versions_sorted};
34 my $last = pop @versions;
35 @versions_to_delete = ($last);
36 } elsif ($params{all_but_notlast}) {
37 pop @versions; # remove last
38 @versions_to_delete = @versions;
40 @versions_to_delete = @versions;
44 foreach my $version (@versions_to_delete) {
45 unlink($version->get_system_location());
56 my ($self, %params) = @_;
58 die 'dbfile not exists' unless ref $params{dbfile} eq 'SL::DB::File';
59 die 'no file contents' unless $params{file_path} || $params{file_contents};
61 my $dbfile = delete $params{dbfile};
63 # Do not save and do not create a new version of the document if file size of last version is the same.
64 if ($dbfile->source eq 'created' && $self->get_version_count(dbfile => $dbfile)) {
65 my $new_file_size = $params{file_path} ? stat($params{file_path})->size : length($params{file_contents});
66 my $last_file_size = stat($self->_filesystem_path($dbfile))->size;
68 return 1 if $last_file_size == $new_file_size;
71 my @versions = @{$dbfile->file_versions_sorted};
72 my $new_version_number = scalar @versions ? $versions[-1]->version + 1 : 1;
74 my $tofile = $self->_filesystem_path($dbfile, $new_version_number);
75 if ($params{file_path} && -f $params{file_path}) {
76 File::Copy::copy($params{file_path}, $tofile);
77 } elsif ($params{file_contents}) {
78 open(OUT, "> " . $tofile);
79 print OUT $params{file_contents};
84 my $doc_path = $::lx_office_conf{paths}->{document_path};
85 my $rel_file = $tofile;
86 $rel_file =~ s/$doc_path//;
87 my $fv = SL::DB::FileVersion->new(
88 file_id => $dbfile->id,
89 version => $new_version_number,
90 file_location => $rel_file,
91 doc_path => $doc_path,
92 backend => 'Filesystem',
93 guid => create_uuid_as_string(UUID_V4),
97 utime($params{mtime}, $params{mtime}, $tofile);
102 sub get_version_count {
103 my ($self, %params) = @_;
104 die "no dbfile" unless $params{dbfile};
105 my $file_id = $params{dbfile}->id;
106 return 0 unless defined $file_id;
107 return SL::DB::Manager::FileVersion->get_all_count(where => [file_id => $file_id]);
111 my ($self, %params) = @_;
112 my $path = $self->get_filepath(%params);
114 my $dt = DateTime->from_epoch(epoch => stat($path)->mtime, time_zone => $::locale->get_local_time_zone()->name, locale => $::lx_office_conf{system}->{language})->clone();
119 my ($self, %params) = @_;
120 die "no dbfile" unless $params{dbfile};
121 my $path = $self->_filesystem_path($params{dbfile},$params{version});
123 die "No file found at $path. Expected: $params{dbfile}{file_name}, file.id: $params{dbfile}{id}" if !-f $path;
129 my ($self, %params) = @_;
130 my $path = $self->get_filepath(%params);
131 return "" unless $path;
132 my $contents = File::Slurp::read_file($path);
137 return 0 unless $::instance_conf->get_doc_files;
138 return 0 unless $::lx_office_conf{paths}->{document_path};
139 return 0 unless -d $::lx_office_conf{paths}->{document_path};
143 sub sync_from_backend {
144 die "Not implemented";
151 sub _filesystem_path {
152 my ($self, $dbfile, $version) = @_;
154 die "No files backend enabled" unless $::instance_conf->get_doc_files || $::lx_office_conf{paths}->{document_path};
157 my $file_version = SL::DB::Manager::FileVersion->get_first(
158 where => [file_id => $dbfile->id],
159 sort_by => 'version DESC'
160 ) or die "Could not find a file version for file with id " . $dbfile->id;
161 $version = $file_version->version;
164 # use filesystem with depth 3
165 my $iddir = sprintf("%04d", $dbfile->id % 1000);
166 my $path = File::Spec->catdir($::lx_office_conf{paths}->{document_path}, $::auth->client->{id}, $iddir, $dbfile->id);
168 File::Path::make_path($path, { chmod => 0770 });
170 return File::Spec->catdir($path, $dbfile->id . '_' . $version);
183 SL::File::Backend::Filesystem - Filesystem class for file storage backend
187 See the synopsis of L<SL::File::Backend>.
191 This specific storage backend use a Filesystem which is only accessed by this interface.
192 This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
193 This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
194 to store the files not to flat in the filesystem. In this Subdirectories for each file an additional subdirectory exists
195 for the versions of this file.
197 The Versioning is done via a Versionnumber which is incremented by one for each version.
198 So the Version 2 of the file with the database id 4 is stored as path {root}/0004/4/4_2.
203 See methods of L<SL::File::Backend>.
211 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>