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