1 package SL::File::Backend::Webdav;
5 use parent qw(SL::File::Backend);
12 use File::Path qw(make_path);
13 use File::MimeInfo::Magic;
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});
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);
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};
47 if ($params{dbfile}->id * 1 == 0) {
49 # new element: need id for file
50 $params{dbfile}->save;
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);
56 elsif ($params{file_contents}) {
57 open(OUT, "> " . $tofile);
58 print OUT $params{file_contents};
64 sub get_version_count {
65 my ($self, %params) = @_;
66 die "no dbfile" unless $params{dbfile};
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: " . $path unless -f $path;
78 my $dt = DateTime->from_epoch(epoch => $st[9])->clone();
79 $main::lxdebug->message(LXDebug->DEBUG2(), "dt=" .$dt);
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 found in Backend: " . $path unless -f $path;
92 my ($self, %params) = @_;
93 my $path = $self->get_filepath(%params);
94 return "" unless $path;
95 my $contents = File::Slurp::read_file($path);
99 sub sync_from_backend {
100 my ($self, %params) = @_;
101 return unless $params{file_type};
103 $self->sync_all_locations(%params);
108 return $::instance_conf->get_doc_webdav;
116 sales_quotation => 'angebote',
117 sales_order => 'bestellungen',
118 request_quotation => 'anfragen',
119 purchase_order => 'lieferantenbestellungen',
120 sales_delivery_order => 'verkaufslieferscheine',
121 purchase_delivery_order => 'einkaufslieferscheine',
122 credit_note => 'gutschriften',
123 invoice => 'rechnungen',
124 purchase_invoice => 'einkaufsrechnungen',
126 service => 'dienstleistungen',
127 assembly => 'erzeugnisse',
129 general_ledger => 'dialogbuchungen',
130 gl_transaction => 'dialogbuchungen',
131 accounts_payable => 'kreditorenbuchungen',
132 shop_image => 'shopbilder',
133 customer => 'kunden',
134 vendor => 'lieferanten',
137 my %type_to_model = (
138 sales_quotation => 'Order',
139 sales_order => 'Order',
140 request_quotation => 'Order',
141 purchase_order => 'Order',
142 sales_delivery_order => 'DeliveryOrder',
143 purchase_delivery_order => 'DeliveryOrder',
144 credit_note => 'Invoice',
145 invoice => 'Invoice',
146 purchase_invoice => 'PurchaseInvoice',
151 general_ledger => 'GLTransaction',
152 gl_transaction => 'GLTransaction',
153 accounts_payable => 'GLTransaction',
154 shop_image => 'Part',
155 customer => 'Customer',
159 my %model_to_number = (
160 Order => 'ordnumber',
161 DeliveryOrder => 'ordnumber',
162 Invoice => 'invnumber',
163 PurchaseInvoice => 'invnumber',
164 Part => 'partnumber',
165 Letter => 'letternumber',
166 GLTransaction => 'reference',
167 ShopImage => 'partnumber',
168 Customer => 'customernumber',
169 Vendor => 'vendornumber',
173 my ($self, $dbfile) = @_;
175 #die "No webdav backend enabled" unless $::instance_conf->get_webdav;
177 my $type = $type_to_path{ $dbfile->object_type };
179 die "Unknown type" unless $type;
181 my $number = $dbfile->backend_data;
183 $number = $self->_get_number_from_model($dbfile);
184 $dbfile->backend_data($number);
187 $main::lxdebug->message(LXDebug->DEBUG2(), "file_name=" . $dbfile->file_name ." number=".$number);
189 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id}, $type, $number);
191 File::Path::make_path($path, { chmod => 0770 });
193 # simply add the timestring before the last .
194 # fails for .tar.gz but the number extraction algorithm failed for all
195 # '123 Storno zu 456' cases and doubled the name like:
196 # Rechnung_123_Storno_zu_456_202113104 Storno zu 456_20211123_113023
197 # TODO extension should be part of the File Model (filetype)
198 my ($filename, $ext) = split(/\.([^\.]+)$/, $dbfile->file_name);
199 my $fname = $filename . '_' . $dbfile->itime->strftime('%Y%m%d_%H%M%S');
200 $fname .= '.' . $ext if $ext;
202 $main::lxdebug->message(LXDebug->DEBUG2(), "webdav path=" . $path . " filename=" . $fname);
204 return (File::Spec->catfile($path, $fname), $path, $fname);
210 #TODO immer noch das alte Problem:
211 #je nachdem von woher der Aufruf kommt ist man in ./users oder .
212 my $rootdir = POSIX::getcwd();
213 my $basename = basename($rootdir);
214 my $dirname = dirname($rootdir);
215 $rootdir = $dirname if $basename eq 'users';
219 sub _get_number_from_model {
220 my ($self, $dbfile) = @_;
222 my $class = 'SL::DB::' . $type_to_model{ $dbfile->object_type };
223 eval "require $class";
224 my $obj = $class->new(id => $dbfile->object_id)->load;
225 die 'no object found' unless $obj;
226 my $numberattr = $model_to_number{ $type_to_model{ $dbfile->object_type } };
227 return $obj->$numberattr;
231 # TODO not fully imlemented and tested
233 sub sync_all_locations {
234 my ($self, %params) = @_;
236 my %dateparms = (dateformat => 'yyyymmdd');
238 foreach my $type (keys %type_to_path) {
241 file_type => $params{file_type},
244 my @oldfiles = @{ SL::DB::Manager::File->get_all(
246 file_type => $params{file_type},
252 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id},$type_to_path{$type});
254 if (opendir my $dir, $path) {
255 foreach my $file (sort { lc $a cmp lc $b }
256 map { decode("UTF-8", $_) } readdir $dir)
258 next if (($file eq '.') || ($file eq '..'));
263 my ($filename, $number, $date, $time_ext) = split(/_/, $fname);
264 my ($time, $ext) = split(/\./, $time_ext, 2);
266 $time = substr($time, 0, 2) . ':' . substr($time, 2, 2) . ':' . substr($time, 4, 2);
268 #my @found = grep { $_->backend_data eq $fname } @oldfiles;
269 #if (scalar(@found) > 0) {
270 # @oldfiles = grep { $_ != @found[0] } @oldfiles;
273 my $dbfile = SL::DB::File->new();
274 my $class = 'SL::DB::Manager::' . $type_to_model{$type};
277 $model_to_number{ $type_to_model{$type} } => $number);
280 my $mime_type = File::MimeInfo::Magic::magic(File::Spec->catfile($path, $fname));
282 # if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix
283 $mime_type = File::MimeInfo::Magic::mimetype($fname);
284 $mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
287 $dbfile->assign_attributes(
288 object_id => $obj->id,
289 object_type => $type,
290 source => $params{file_type} eq 'document' ? 'created' : 'uploaded',
291 file_type => $params{file_type},
292 file_name => $filename . '_' . $number . '_' . $ext,
293 mime_type => $mime_type,
294 itime => $::locale->parse_date_to_object($date . ' ' . $time, %dateparms),
316 SL::File::Backend::Filesystem - Filesystem class for file storage backend
320 See the synopsis of L<SL::File::Backend>.
324 This specific storage backend use a Filesystem which is only accessed by this interface.
325 This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
326 This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
327 to store the files not to flat in the filesystem.
332 See methods of L<SL::File::Backend>.
340 The synchronization must be tested and a periodical task is needed to synchronize in some time periods.
344 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>