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