1 package SL::File::Backend::Filesystem;
5 use parent qw(SL::File::Backend);
10 use File::Path qw(make_path);
17 my ($self, %params) = @_;
18 die "no dbfile in backend delete" unless $params{dbfile};
19 my $last_version = $params{dbfile}->backend_data;
20 my $first_version = 1;
21 $last_version = 0 if $params{last};
22 $last_version = $params{dbfile}->backend_data-1 if $params{all_but_notlast};
23 $last_version = $params{version} if $params{version};
24 $first_version = $params{version} if $params{version};
26 if ($last_version > 0 ) {
27 for my $version ( $first_version..$last_version) {
28 my $file_path = $self->_filesystem_path($params{dbfile},$version);
31 if ($params{version}) {
32 for my $version ( $last_version+1 .. $params{dbfile}->backend_data) {
33 my $from = $self->_filesystem_path($params{dbfile},$version);
34 my $to = $self->_filesystem_path($params{dbfile},$version - 1);
35 die "file not exists in backend delete" unless -f $from;
38 $params{dbfile}->backend_data($params{dbfile}->backend_data-1);
40 elsif ($params{all_but_notlast}) {
41 my $from = $self->_filesystem_path($params{dbfile},$params{dbfile}->backend_data);
42 my $to = $self->_filesystem_path($params{dbfile},1);
43 die "file not exists in backend delete" unless -f $from;
45 $params{dbfile}->backend_data(1);
47 $params{dbfile}->backend_data(0);
49 unless ($params{dbfile}->backend_data) {
50 my $dir_path = $self->_filesystem_path($params{dbfile});
54 my $file_path = $self->_filesystem_path($params{dbfile},$params{dbfile}->backend_data);
55 die "file not exists in backend delete" unless -f $file_path;
57 $params{dbfile}->backend_data($params{dbfile}->backend_data-1);
66 my ($self, %params) = @_;
67 die 'dbfile not exists' unless $params{dbfile};
68 my $dbfile = $params{dbfile};
69 die 'no file contents' unless $params{file_path} || $params{file_contents};
71 # Do not save and do not create a new version of the document if file size of last version is the same.
72 if ($dbfile->source eq 'created' && $self->get_version_count(dbfile => $dbfile)) {
73 my $new_file_size = $params{file_path} ? stat($params{file_path})->size : length($params{file_contents});
74 my $last_file_size = stat($self->_filesystem_path($dbfile))->size;
76 return 1 if $last_file_size == $new_file_size;
79 $dbfile->backend_data(0) unless $dbfile->backend_data;
80 $dbfile->backend_data($dbfile->backend_data*1+1);
83 my $tofile = $self->_filesystem_path($dbfile);
84 if ($params{file_path} && -f $params{file_path}) {
85 File::Copy::copy($params{file_path}, $tofile);
87 elsif ($params{file_contents}) {
88 open(OUT, "> " . $tofile);
89 print OUT $params{file_contents};
93 utime($params{mtime}, $params{mtime}, $tofile);
98 sub get_version_count {
99 my ($self, %params) = @_;
100 die "no dbfile" unless $params{dbfile};
101 return $params{dbfile}->backend_data//0 * 1;
105 my ($self, %params) = @_;
106 die "no dbfile" unless $params{dbfile};
107 die "unknown version" if $params{version} &&
108 ($params{version} < 0 || $params{version} > $params{dbfile}->backend_data);
109 my $path = $self->_filesystem_path($params{dbfile}, $params{version});
111 die "No file found at $path. Expected: $params{dbfile}{file_name}, file.id: $params{dbfile}{id}" if !-f $path;
113 my $dt = DateTime->from_epoch(epoch => stat($path)->mtime, time_zone => $::locale->get_local_time_zone()->name, locale => $::lx_office_conf{system}->{language})->clone();
118 my ($self, %params) = @_;
119 die "no dbfile" unless $params{dbfile};
120 my $path = $self->_filesystem_path($params{dbfile},$params{version});
122 die "No file found at $path. Expected: $params{dbfile}{file_name}, file.id: $params{dbfile}{id}" if !-f $path;
128 my ($self, %params) = @_;
129 my $path = $self->get_filepath(%params);
130 return "" unless $path;
131 my $contents = File::Slurp::read_file($path);
136 return 0 unless $::instance_conf->get_doc_files;
137 return 0 unless $::lx_office_conf{paths}->{document_path};
138 return 0 unless -d $::lx_office_conf{paths}->{document_path};
142 sub sync_from_backend {
143 my ($self, %params) = @_;
144 my @query = (file_type => $params{file_type});
145 push @query, (file_name => $params{file_name}) if $params{file_name};
146 push @query, (mime_type => $params{mime_type}) if $params{mime_type};
147 push @query, (source => $params{source}) if $params{source};
149 my $sortby = $params{sort_by} || 'itime DESC,file_name ASC';
151 my @files = @{ SL::DB::Manager::File->get_all(query => [@query], sort_by => $sortby) };
153 $main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $_->id." version=".$_->backend_data);
154 my $newversion = $_->backend_data;
155 for my $version ( reverse 1 .. $_->backend_data ) {
156 my $path = $self->_filesystem_path($_, $version);
157 $main::lxdebug->message(LXDebug->DEBUG2(), "path=".$path." exists=".( -f $path?1:0));
159 $newversion = $version - 1;
161 $main::lxdebug->message(LXDebug->DEBUG2(), "newversion=".$newversion." version=".$_->backend_data);
162 if ( $newversion < $_->backend_data ) {
163 $_->backend_data($newversion);
164 $_->save if $newversion > 0;
165 $_->delete if $newversion <= 0;
175 sub _filesystem_path {
176 my ($self, $dbfile, $version) = @_;
178 die "No files backend enabled" unless $::instance_conf->get_doc_files || $::lx_office_conf{paths}->{document_path};
180 # use filesystem with depth 3
181 $version = $dbfile->backend_data if !$version || $version < 1 || $version > $dbfile->backend_data;
182 my $iddir = sprintf("%04d", $dbfile->id % 1000);
183 my $path = File::Spec->catdir($::lx_office_conf{paths}->{document_path}, $::auth->client->{id}, $iddir, $dbfile->id);
185 File::Path::make_path($path, { chmod => 0770 });
187 return $path if !$version;
188 return File::Spec->catdir($path, $dbfile->id . '_' . $version);
201 SL::File::Backend::Filesystem - Filesystem class for file storage backend
205 See the synopsis of L<SL::File::Backend>.
209 This specific storage backend use a Filesystem which is only accessed by this interface.
210 This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
211 This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
212 to store the files not to flat in the filesystem. In this Subdirectories for each file an additional subdirectory exists
213 for the versions of this file.
215 The Versioning is done via a Versionnumber which is incremented by one for each version.
216 So the Version 2 of the file with the database id 4 is stored as path {root}/0004/4/4_2.
221 See methods of L<SL::File::Backend>.
229 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>