Revision 0e9f27e4
Von Martin Helmling martin.helmling@octosoft.eu vor fast 8 Jahren hinzugefügt
SL/File/Backend/Webdav.pm | ||
---|---|---|
1 |
package SL::File::Backend::Webdav; |
|
2 |
|
|
3 |
use strict; |
|
4 |
|
|
5 |
use parent qw(SL::File::Backend); |
|
6 |
use SL::DB::File; |
|
7 |
|
|
8 |
#use SL::Webdav; |
|
9 |
use File::Copy; |
|
10 |
use File::Slurp; |
|
11 |
use File::Basename; |
|
12 |
use File::Path qw(make_path); |
|
13 |
use File::MimeInfo::Magic; |
|
14 |
|
|
15 |
# |
|
16 |
# public methods |
|
17 |
# |
|
18 |
|
|
19 |
sub delete { |
|
20 |
my ($self, %params) = @_; |
|
21 |
$main::lxdebug->message(LXDebug->DEBUG2(), "del in backend " . $self . " file " . $params{dbfile}); |
|
22 |
$main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $params{dbfile}->id * 1); |
|
23 |
return 0 unless $params{dbfile}; |
|
24 |
my ($file_path, undef, undef) = $self->webdav_path($params{dbfile}); |
|
25 |
unlink($file_path); |
|
26 |
return 1; |
|
27 |
} |
|
28 |
|
|
29 |
sub rename { |
|
30 |
my ($self, %params) = @_; |
|
31 |
return 0 unless $params{dbfile}; |
|
32 |
my (undef, $oldwebdavname) = split(/:/, $params{dbfile}->location, 2); |
|
33 |
my ($tofile, $basepath, $basename) = $self->webdav_path($params{dbfile}); |
|
34 |
my $fromfile = File::Spec->catfile($basepath, $oldwebdavname); |
|
35 |
$main::lxdebug->message(LXDebug->DEBUG2(), "renamefrom=" . $fromfile . " to=" . $tofile); |
|
36 |
move($fromfile, $tofile); |
|
37 |
} |
|
38 |
|
|
39 |
sub save { |
|
40 |
my ($self, %params) = @_; |
|
41 |
die 'dbfile not exists' unless $params{dbfile}; |
|
42 |
$main::lxdebug->message(LXDebug->DEBUG2(), "in backend " . $self . " file " . $params{dbfile}); |
|
43 |
$main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $params{dbfile}->id); |
|
44 |
my $dbfile = $params{dbfile}; |
|
45 |
die 'no file contents' unless $params{file_path} || $params{file_contents}; |
|
46 |
|
|
47 |
if ($params{dbfile}->id * 1 == 0) { |
|
48 |
|
|
49 |
# new element: need id for file |
|
50 |
$params{dbfile}->save; |
|
51 |
} |
|
52 |
my ($tofile, undef, $basename) = $self->webdav_path($params{dbfile}); |
|
53 |
if ($params{file_path} && -f $params{file_path}) { |
|
54 |
copy($params{file_path}, $tofile); |
|
55 |
} |
|
56 |
elsif ($params{file_contents}) { |
|
57 |
open(OUT, "> " . $tofile); |
|
58 |
print OUT $params{file_contents}; |
|
59 |
close(OUT); |
|
60 |
} |
|
61 |
return 1; |
|
62 |
} |
|
63 |
|
|
64 |
sub get_version_count { |
|
65 |
my ($self, %params) = @_; |
|
66 |
die "no dbfile" unless $params{dbfile}; |
|
67 |
## TODO |
|
68 |
return 1; |
|
69 |
} |
|
70 |
|
|
71 |
sub get_mtime { |
|
72 |
my ($self, %params) = @_; |
|
73 |
die "no dbfile" unless $params{dbfile}; |
|
74 |
$main::lxdebug->message(LXDebug->DEBUG2(), "version=" .$params{version}); |
|
75 |
my ($path, undef, undef) = $self->webdav_path($params{dbfile}); |
|
76 |
die "no file found in backend" if !-f $path; |
|
77 |
my @st = stat($path); |
|
78 |
my $dt = DateTime->from_epoch(epoch => $st[9])->clone(); |
|
79 |
$main::lxdebug->message(LXDebug->DEBUG2(), "dt=" .$dt); |
|
80 |
return $dt; |
|
81 |
} |
|
82 |
|
|
83 |
sub get_filepath { |
|
84 |
my ($self, %params) = @_; |
|
85 |
die "no dbfile" unless $params{dbfile}; |
|
86 |
my ($path, undef, undef) = $self->webdav_path($params{dbfile}); |
|
87 |
die "no file" if !-f $path; |
|
88 |
return $path; |
|
89 |
} |
|
90 |
|
|
91 |
sub get_content { |
|
92 |
my ($self, %params) = @_; |
|
93 |
my $path = $self->get_filepath(%params); |
|
94 |
return "" unless $path; |
|
95 |
my $contents = File::Slurp::read_file($path); |
|
96 |
return \$contents; |
|
97 |
} |
|
98 |
|
|
99 |
sub sync_from_backend { |
|
100 |
my ($self, %params) = @_; |
|
101 |
return unless $params{file_type}; |
|
102 |
|
|
103 |
$self->sync_all_locations(%params); |
|
104 |
|
|
105 |
} |
|
106 |
|
|
107 |
sub enabled { |
|
108 |
return 0 unless $::instance_conf->get_doc_webdav; |
|
109 |
return 1; |
|
110 |
} |
|
111 |
|
|
112 |
# |
|
113 |
# internals |
|
114 |
# |
|
115 |
|
|
116 |
my %type_to_path = ( |
|
117 |
sales_quotation => 'angebote', |
|
118 |
sales_order => 'bestellungen', |
|
119 |
request_quotation => 'anfragen', |
|
120 |
purchase_order => 'lieferantenbestellungen', |
|
121 |
sales_delivery_order => 'verkaufslieferscheine', |
|
122 |
purchase_delivery_order => 'einkaufslieferscheine', |
|
123 |
credit_note => 'gutschriften', |
|
124 |
invoice => 'rechnungen', |
|
125 |
purchase_invoice => 'einkaufsrechnungen', |
|
126 |
part => 'waren', |
|
127 |
service => 'dienstleistungen', |
|
128 |
assembly => 'erzeugnisse', |
|
129 |
letter => 'briefe', |
|
130 |
general_ledger => 'dialogbuchungen', |
|
131 |
accounts_payable => 'kreditorenbuchungen', |
|
132 |
); |
|
133 |
|
|
134 |
my %type_to_model = ( |
|
135 |
sales_quotation => 'Order', |
|
136 |
sales_order => 'Order', |
|
137 |
request_quotation => 'Order', |
|
138 |
purchase_order => 'Order', |
|
139 |
sales_delivery_order => 'DeliveryOrder', |
|
140 |
purchase_delivery_order => 'DeliveryOrder', |
|
141 |
credit_note => 'Invoice', |
|
142 |
invoice => 'Invoice', |
|
143 |
purchase_invoice => 'PurchaseInvoice', |
|
144 |
part => 'Part', |
|
145 |
service => 'Part', |
|
146 |
assembly => 'Part', |
|
147 |
letter => 'Letter', |
|
148 |
general_ledger => 'GLTransaction', |
|
149 |
accounts_payable => 'GLTransaction', |
|
150 |
); |
|
151 |
|
|
152 |
my %model_to_number = ( |
|
153 |
Order => 'ordnumber', |
|
154 |
DeliveryOrder => 'ordnumber', |
|
155 |
Invoice => 'invnumber', |
|
156 |
PurchaseInvoice => 'invnumber', |
|
157 |
Part => 'partnumber', |
|
158 |
Letter => 'letternumber', |
|
159 |
GLTransaction => 'reference' |
|
160 |
); |
|
161 |
|
|
162 |
sub webdav_path { |
|
163 |
my ($self, $dbfile) = @_; |
|
164 |
|
|
165 |
#die "No webdav backend enabled" unless $::instance_conf->get_webdav; |
|
166 |
|
|
167 |
my $type = $type_to_path{ $dbfile->object_type }; |
|
168 |
|
|
169 |
die "Unknown type" unless $type; |
|
170 |
|
|
171 |
my $number = $dbfile->backend_data; |
|
172 |
if ($number eq '') { |
|
173 |
$number = $self->_get_number_from_model($dbfile); |
|
174 |
$dbfile->backend_data($number); |
|
175 |
$dbfile->save; |
|
176 |
} |
|
177 |
$main::lxdebug->message(LXDebug->DEBUG2(), "file_name=" . $dbfile->file_name ." number=".$number); |
|
178 |
|
|
179 |
my @fileparts = split(/_/, $dbfile->file_name); |
|
180 |
my $number_ext = pop @fileparts; |
|
181 |
my ($maynumber, $ext) = split(/\./, $number_ext, 2); |
|
182 |
push @fileparts, $maynumber if $maynumber ne $number; |
|
183 |
|
|
184 |
my $basename = join('_', @fileparts); |
|
185 |
|
|
186 |
my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id}, $type, $number); |
|
187 |
if (!-d $path) { |
|
188 |
File::Path::make_path($path, { chmod => 0770 }); |
|
189 |
} |
|
190 |
my $fname = $basename . '_' . $number . '_' . $dbfile->itime->strftime('%Y%m%d_%H%M%S'); |
|
191 |
$fname .= '.' . $ext if $ext; |
|
192 |
|
|
193 |
$main::lxdebug->message(LXDebug->DEBUG2(), "webdav path=" . $path . " filename=" . $fname); |
|
194 |
|
|
195 |
return (File::Spec->catfile($path, $fname), $path, $fname); |
|
196 |
} |
|
197 |
|
|
198 |
sub get_rootdir { |
|
199 |
my ($self) = @_; |
|
200 |
|
|
201 |
#TODO immer noch das alte Problem: |
|
202 |
#je nachdem von woher der Aufruf kommt ist man in ./users oder . |
|
203 |
my $rootdir = POSIX::getcwd(); |
|
204 |
my $basename = basename($rootdir); |
|
205 |
my $dirname = dirname($rootdir); |
|
206 |
$rootdir = $dirname if $basename eq 'users'; |
|
207 |
return $rootdir; |
|
208 |
} |
|
209 |
|
|
210 |
sub _get_number_from_model { |
|
211 |
my ($self, $dbfile) = @_; |
|
212 |
|
|
213 |
my $class = 'SL::DB::' . $type_to_model{ $dbfile->object_type }; |
|
214 |
eval "require $class"; |
|
215 |
my $obj = $class->new(id => $dbfile->object_id)->load; |
|
216 |
die 'no object found' unless $obj; |
|
217 |
my $numberattr = $model_to_number{ $type_to_model{ $dbfile->object_type } }; |
|
218 |
return $obj->$numberattr; |
|
219 |
} |
|
220 |
|
|
221 |
# |
|
222 |
# TODO not fully imlemented and tested |
|
223 |
# |
|
224 |
sub sync_all_locations { |
|
225 |
my ($self, %params) = @_; |
|
226 |
|
|
227 |
my %dateparms = (dateformat => 'yyyymmdd'); |
|
228 |
|
|
229 |
foreach my $type (keys %type_to_path) { |
|
230 |
|
|
231 |
my @query = ( |
|
232 |
file_type => $params{file_type}, |
|
233 |
object_type => $type |
|
234 |
); |
|
235 |
my @oldfiles = @{ SL::DB::Manager::File->get_all( |
|
236 |
query => [ |
|
237 |
file_type => $params{file_type}, |
|
238 |
object_type => $type |
|
239 |
] |
|
240 |
) |
|
241 |
}; |
|
242 |
|
|
243 |
my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id},$type_to_path{$type}); |
|
244 |
|
|
245 |
if (opendir my $dir, $path) { |
|
246 |
foreach my $file (sort { lc $a cmp lc $b } |
|
247 |
map { decode("UTF-8", $_) } readdir $dir) |
|
248 |
{ |
|
249 |
next if (($file eq '.') || ($file eq '..')); |
|
250 |
|
|
251 |
my $fname = $file; |
|
252 |
$fname =~ s|.*/||; |
|
253 |
|
|
254 |
my ($filename, $number, $date, $time_ext) = split(/_/, $fname); |
|
255 |
my ($time, $ext) = split(/\./, $time_ext, 2); |
|
256 |
|
|
257 |
$time = substr($time, 0, 2) . ':' . substr($time, 2, 2) . ':' . substr($time, 4, 2); |
|
258 |
|
|
259 |
#my @found = grep { $_->backend_data eq $fname } @oldfiles; |
|
260 |
#if (scalar(@found) > 0) { |
|
261 |
# @oldfiles = grep { $_ != @found[0] } @oldfiles; |
|
262 |
#} |
|
263 |
#else { |
|
264 |
my $dbfile = SL::DB::File->new(); |
|
265 |
my $class = 'SL::DB::Manager::' . $type_to_model{$type}; |
|
266 |
my $obj = |
|
267 |
$class->find_by( |
|
268 |
$model_to_number{ $type_to_model{$type} } => $number); |
|
269 |
if ($obj) { |
|
270 |
|
|
271 |
my $mime_type = File::MimeInfo::Magic::magic(File::Spec->catfile($path, $fname)); |
|
272 |
if (!$mime_type) { |
|
273 |
# if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix |
|
274 |
$mime_type = File::MimeInfo::Magic::mimetype($fname); |
|
275 |
$mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type; |
|
276 |
} |
|
277 |
|
|
278 |
$dbfile->assign_attributes( |
|
279 |
object_id => $obj->id, |
|
280 |
object_type => $type, |
|
281 |
source => $params{file_type} eq 'document' ? 'created' : 'uploaded', |
|
282 |
file_type => $params{file_type}, |
|
283 |
file_name => $filename . '_' . $number . '_' . $ext, |
|
284 |
mime_type => $mime_type, |
|
285 |
itime => $::locale->parse_date_to_object($date . ' ' . $time, %dateparms), |
|
286 |
); |
|
287 |
$dbfile->save; |
|
288 |
} |
|
289 |
#} |
|
290 |
|
|
291 |
closedir $dir; |
|
292 |
} |
|
293 |
} |
|
294 |
} |
|
295 |
} |
|
296 |
|
|
297 |
1; |
|
298 |
|
|
299 |
__END__ |
|
300 |
|
|
301 |
=pod |
|
302 |
|
|
303 |
=encoding utf8 |
|
304 |
|
|
305 |
=head1 NAME |
|
306 |
|
|
307 |
SL::File::Backend::Filesystem - Filesystem class for file storage backend |
|
308 |
|
|
309 |
=head1 SYNOPSIS |
|
310 |
|
|
311 |
See the synopsis of L<SL::File::Backend>. |
|
312 |
|
|
313 |
=head1 OVERVIEW |
|
314 |
|
|
315 |
This specific storage backend use a Filesystem which is only accessed by this interface. |
|
316 |
This is the big difference to the Webdav backend where the files can be accessed without the control of that backend. |
|
317 |
This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories |
|
318 |
to store the files not to flat in the filesystem. |
|
319 |
|
|
320 |
|
|
321 |
=head1 METHODS |
|
322 |
|
|
323 |
See methods of L<SL::File::Backend>. |
|
324 |
|
|
325 |
=head1 SEE ALSO |
|
326 |
|
|
327 |
L<SL::File::Backend> |
|
328 |
|
|
329 |
=head1 TODO |
|
330 |
|
|
331 |
The synchronization must be tested and a periodical task is needed to synchronize in some time periods. |
|
332 |
|
|
333 |
=head1 AUTHOR |
|
334 |
|
|
335 |
Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt> |
|
336 |
|
|
337 |
=cut |
|
338 |
|
|
339 |
|
Auch abrufbar als: Unified diff
Dateimanagement: Webdav Backend (NICHT FERTIG !!!)
Dieses Backend bedarf noch Entwicklung
Die Synchronisierung ist noch nicht fertig implementiert und getestet