Filemanagement: Löschen von DMS-Anhängen wirft Fehler
[kivitendo-erp.git] / SL / File / Backend / Filesystem.pm
1 package SL::File::Backend::Filesystem;
2
3 use strict;
4
5 use parent qw(SL::File::Backend);
6 use SL::DB::File;
7 use File::Copy;
8 use File::Slurp;
9 use File::Path qw(make_path);
10
11 #
12 # public methods
13 #
14
15 sub delete {
16   my ($self, %params) = @_;
17   $main::lxdebug->message(LXDebug->DEBUG2(), "del in backend " . $self . "  file " . $params{dbfile});
18   $main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . ($params{dbfile}->id * 1));
19   $main::lxdebug->message(LXDebug->DEBUG2(), "last=" .  $params{last}." all_bnl=". $params{all_but_notlast});
20   die "no dbfile in backend delete" unless $params{dbfile};
21   my $backend_data = $params{dbfile}->backend_data;
22   $backend_data    = 0                               if $params{last};
23   $backend_data    = $params{dbfile}->backend_data-1 if $params{all_but_notlast};
24
25   if ($backend_data > 0 ) {
26     $main::lxdebug->message(LXDebug->DEBUG2(), "backend_data=" .$backend_data);
27     for my $version ( 1..$backend_data) {
28       my $file_path = $self->_filesystem_path($params{dbfile},$version);
29       $main::lxdebug->message(LXDebug->DEBUG2(), "unlink " .$file_path);
30       unlink($file_path);
31     }
32     if ($params{all_but_notlast}) {
33       my $from = $self->_filesystem_path($params{dbfile},$params{dbfile}->backend_data);
34       my $to   = $self->_filesystem_path($params{dbfile},1);
35       die "file not exists in backend delete" unless -f $from;
36       rename($from,$to);
37       $params{dbfile}->backend_data(1);
38     } else {
39       $params{dbfile}->backend_data(0);
40       my $dir_path = $self->_filesystem_path($params{dbfile});
41       rmdir($dir_path);
42       $main::lxdebug->message(LXDebug->DEBUG2(), "rmdir " .$dir_path);
43     }
44   } else {
45     my $file_path = $self->_filesystem_path($params{dbfile},$params{dbfile}->backend_data);
46     die "file not exists in backend delete" unless -f $file_path;
47     unlink($file_path);
48     $params{dbfile}->backend_data($params{dbfile}->backend_data-1);
49   }
50   return 1;
51 }
52
53 sub rename {
54 }
55
56 sub save {
57   my ($self, %params) = @_;
58   die 'dbfile not exists' unless $params{dbfile};
59   my $dbfile = $params{dbfile};
60   die 'no file contents' unless $params{file_path} || $params{file_contents};
61   $dbfile->backend_data(0) unless $dbfile->backend_data;
62   $dbfile->backend_data($dbfile->backend_data*1+1);
63   $dbfile->save->load;
64
65   my $tofile = $self->_filesystem_path($dbfile);
66   if ($params{file_path} && -f $params{file_path}) {
67     File::Copy::copy($params{file_path}, $tofile);
68   }
69   elsif ($params{file_contents}) {
70     open(OUT, "> " . $tofile);
71     print OUT $params{file_contents};
72     close(OUT);
73   }
74   return 1;
75 }
76
77 sub get_version_count {
78   my ($self, %params) = @_;
79   die "no dbfile" unless $params{dbfile};
80   return $params{dbfile}->backend_data * 1;
81 }
82
83 sub get_mtime {
84   my ($self, %params) = @_;
85   die "no dbfile" unless $params{dbfile};
86   $main::lxdebug->message(LXDebug->DEBUG2(), "version=" .$params{version});
87   die "unknown version" if $params{version} &&
88                           ($params{version} < 0 || $params{version} > $params{dbfile}->backend_data) ;
89   my $path = $self->_filesystem_path($params{dbfile},$params{version});
90   die "no file found in backend get_mtime" if !-f $path;
91   my @st = stat($path);
92   my $dt = DateTime->from_epoch(epoch => $st[9])->clone();
93   $main::lxdebug->message(LXDebug->DEBUG2(), "dt=" .$dt);
94   return $dt;
95 }
96
97 sub get_filepath {
98   my ($self, %params) = @_;
99   die "no dbfile" unless $params{dbfile};
100   my $path = $self->_filesystem_path($params{dbfile},$params{version});
101   die "no file in backend get_filepath" if !-f $path;
102   return $path;
103 }
104
105 sub get_content {
106   my ($self, %params) = @_;
107   my $path = $self->get_filepath(%params);
108   return "" unless $path;
109   my $contents = File::Slurp::read_file($path);
110   return \$contents;
111 }
112
113 sub enabled {
114   return 0 unless $::instance_conf->get_doc_files;
115   return 0 unless $::lx_office_conf{paths}->{document_path};
116   return 0 unless -d $::lx_office_conf{paths}->{document_path};
117   return 1;
118 }
119
120
121 #
122 # internals
123 #
124
125 sub _filesystem_path {
126   my ($self, $dbfile, $version) = @_;
127
128   die "No files backend enabled" unless $::instance_conf->get_doc_files || $::lx_office_conf{paths}->{document_path};
129
130   # use filesystem with depth 3
131   $version    = $dbfile->backend_data if !$version || $version < 1 || $version > $dbfile->backend_data;
132   my $iddir   = sprintf("%04d", $dbfile->id % 1000);
133   my $path    = File::Spec->catdir($::lx_office_conf{paths}->{document_path}, $::auth->client->{id}, $iddir, $dbfile->id);
134   $main::lxdebug->message(LXDebug->DEBUG2(), "file path=" .$path." id=" .$dbfile->id." version=".$version." basename=".$dbfile->id . '_' . $version);
135   if (!-d $path) {
136     File::Path::make_path($path, { chmod => 0770 });
137   }
138   return $path if !$version;
139   return File::Spec->catdir($path, $dbfile->id . '_' . $version);
140 }
141
142 1;
143
144 __END__
145
146 =pod
147
148 =encoding utf8
149
150 =head1 NAME
151
152 SL::File::Backend::Filesystem  - Filesystem class for file storage backend
153
154 =head1 SYNOPSIS
155
156 See the synopsis of L<SL::File::Backend>.
157
158 =head1 OVERVIEW
159
160 This specific storage backend use a Filesystem which is only accessed by this interface.
161 This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
162 This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
163 to store the files not to flat in the filesystem. In this Subdirectories for each file an additional subdirectory exists
164 for the versions of this file.
165
166 The Versioning is done via a Versionnumber which is incremented by one for each version.
167 So the Version 2 of the file with the database id 4 is stored as path {root}/0004/4/4_2.
168
169
170 =head1 METHODS
171
172 See methods of L<SL::File::Backend>.
173
174 =head1 SEE ALSO
175
176 L<SL::File::Backend>
177
178 =head1 AUTHOR
179
180 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
181
182 =cut