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