1 package SL::File::Backend::Webdav;
5 use parent qw(SL::File::Backend);
7 use SL::DB::FileVersion;
9 use SL::System::Process;
13 use File::Path qw(make_path);
14 use File::MimeInfo::Magic;
16 use UUID::Tiny ':std';
23 my ($self, %params) = @_;
24 $main::lxdebug->message(LXDebug->DEBUG2(), "del in backend " . $self . " file " . $params{dbfile});
25 $main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $params{dbfile}->id * 1);
26 return 0 unless $params{dbfile};
27 my ($file_path, undef, undef) = $self->webdav_path($params{dbfile});
33 my ($self, %params) = @_;
34 return 0 unless $params{dbfile};
35 my (undef, $oldwebdavname) = split(/:/, $params{dbfile}->location, 2);
36 my ($tofile, $basepath, $basename) = $self->webdav_path($params{dbfile});
37 my $fromfile = File::Spec->catfile($basepath, $oldwebdavname);
38 $main::lxdebug->message(LXDebug->DEBUG2(), "renamefrom=" . $fromfile . " to=" . $tofile);
39 move($fromfile, $tofile);
43 my ($self, %params) = @_;
44 die 'dbfile not exists' unless $params{dbfile};
45 $main::lxdebug->message(LXDebug->DEBUG2(), "in backend " . $self . " file " . $params{dbfile});
46 $main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $params{dbfile}->id);
47 my $dbfile = $params{dbfile};
48 die 'no file contents' unless $params{file_path} || $params{file_contents};
50 if ($params{dbfile}->id * 1 == 0) {
52 # new element: need id for file
53 $params{dbfile}->save;
55 my ($tofile, undef, $basename) = $self->webdav_path($params{dbfile});
56 if ($params{file_path} && -f $params{file_path}) {
57 copy($params{file_path}, $tofile);
59 elsif ($params{file_contents}) {
60 open(OUT, "> " . $tofile);
61 print OUT $params{file_contents};
66 my $doc_path = $self->get_rootdir();
67 my $rel_file = $tofile;
68 $rel_file =~ s/$doc_path//;
69 my $fv = SL::DB::FileVersion->new(
70 file_id => $params{dbfile}->id,
71 version => 1, # Webdav doesn't have versions by now.
72 file_location => $rel_file,
73 doc_path => $doc_path,
75 guid => create_uuid_as_string(UUID_V4),
81 sub get_version_count {
82 my ($self, %params) = @_;
83 die "no dbfile" unless $params{dbfile};
84 # TODO: Webdav doesn't have versions by now.
85 my ($path, undef, undef) = $self->webdav_path($params{dbfile});
86 return (-f $path || 0) * 1; # return 1 if file is found otherwise 0
90 my ($self, %params) = @_;
91 die "no dbfile" unless $params{dbfile};
92 $main::lxdebug->message(LXDebug->DEBUG2(), "version=" .$params{version});
93 my ($path, undef, undef) = $self->webdav_path($params{dbfile});
94 die "No file found in Backend: " . $path unless -f $path;
95 my $dt = DateTime->from_epoch(epoch => stat($path)->mtime, time_zone => $::locale->get_local_time_zone()->name)->clone();
96 $main::lxdebug->message(LXDebug->DEBUG2(), "dt=" .$dt);
101 my ($self, %params) = @_;
102 die "no dbfile" unless $params{dbfile};
103 my ($path, undef, undef) = $self->webdav_path($params{dbfile});
104 die "No file found in Backend: " . $path unless -f $path;
109 my ($self, %params) = @_;
110 my $path = $self->get_filepath(%params);
111 return "" unless $path;
112 my $contents = File::Slurp::read_file($path);
116 sub sync_from_backend {
117 my ($self, %params) = @_;
118 return unless $params{file_type};
120 $self->sync_all_locations(%params);
125 return $::instance_conf->get_doc_webdav;
133 sales_quotation => 'angebote',
134 sales_order_intake => 'auftragseingaenge',
135 sales_order => 'bestellungen',
136 request_quotation => 'anfragen',
137 purchase_quotation_intake => 'angebotseingaenge',
138 purchase_order => 'lieferantenbestellungen',
139 purchase_order_confirmation => 'lieferantenauftragsbestaetigungen',
140 sales_delivery_order => 'verkaufslieferscheine',
141 purchase_delivery_order => 'einkaufslieferscheine',
142 purchase_reclamation => 'einkaufsreklamation',
143 sales_reclamation => 'verkaufsreklamation',
144 supplier_delivery_order => 'beistelllieferscheine',
145 rma_delivery_order => 'retourenlieferscheine',
146 credit_note => 'gutschriften',
147 invoice => 'rechnungen',
148 invoice_for_advance_payment => 'rechnungen',
149 final_invoice => 'rechnungen',
150 purchase_invoice => 'einkaufsrechnungen',
152 service => 'dienstleistungen',
153 assembly => 'erzeugnisse',
155 general_ledger => 'dialogbuchungen',
156 gl_transaction => 'dialogbuchungen',
157 accounts_payable => 'kreditorenbuchungen',
158 shop_image => 'shopbilder',
159 customer => 'kunden',
160 vendor => 'lieferanten',
163 my %type_to_model = (
164 sales_quotation => 'Order',
165 sales_order_intake => 'Order',
166 sales_order => 'Order',
167 request_quotation => 'Order',
168 purchase_quotation_intake => 'Order',
169 purchase_order => 'Order',
170 sales_delivery_order => 'DeliveryOrder',
171 purchase_delivery_order => 'DeliveryOrder',
172 sales_reclamation => 'Reclamation',
173 purchase_reclamation => 'Reclamation',
174 supplier_delivery_order => 'DeliveryOrder',
175 rma_delivery_order => 'DeliveryOrder',
176 credit_note => 'Invoice',
177 invoice => 'Invoice',
178 invoice_for_advance_payment => 'Invoice',
179 final_invoice => 'Invoice',
180 purchase_invoice => 'PurchaseInvoice',
185 general_ledger => 'GLTransaction',
186 gl_transaction => 'GLTransaction',
187 accounts_payable => 'GLTransaction',
188 shop_image => 'Part',
189 customer => 'Customer',
193 my %model_to_number = (
194 Order => 'record_number',
195 DeliveryOrder => 'record_number',
196 Reclamation => 'record_number',
197 Invoice => 'invnumber',
198 PurchaseInvoice => 'invnumber',
199 Part => 'partnumber',
200 Letter => 'letternumber',
201 GLTransaction => 'reference',
202 ShopImage => 'partnumber',
203 Customer => 'customernumber',
204 Vendor => 'vendornumber',
208 my ($self, $dbfile) = @_;
210 #die "No webdav backend enabled" unless $::instance_conf->get_webdav;
212 my $type = $type_to_path{ $dbfile->object_type };
214 die "Unknown type" unless $type;
216 my $number = $dbfile->backend_data;
218 $number = $self->_get_number_from_model($dbfile);
219 $dbfile->backend_data($number);
222 $number =~ s/\//-/g; # replace forbidden char;
223 $main::lxdebug->message(LXDebug->DEBUG2(), "file_name=" . $dbfile->file_name ." number=".$number);
225 my @fileparts = split(/_/, $dbfile->file_name);
226 my $number_ext = pop @fileparts;
227 my ($maynumber, $ext) = split(/\./, $number_ext, 2);
228 push @fileparts, $maynumber if $maynumber ne $number;
230 my $basename = join('_', @fileparts);
232 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id}, $type, $number);
234 File::Path::make_path($path, { chmod => 0770 });
236 my $fname = $basename . '_' . $number . '_' . $dbfile->itime->strftime('%Y%m%d_%H%M%S');
237 $fname .= '.' . $ext if $ext;
239 $main::lxdebug->message(LXDebug->DEBUG2(), "webdav path=" . $path . " filename=" . $fname);
241 return (File::Spec->catfile($path, $fname), $path, $fname);
244 sub get_rootdir { SL::System::Process::exe_dir() }
246 sub _get_number_from_model {
247 my ($self, $dbfile) = @_;
249 my $class = 'SL::DB::' . $type_to_model{ $dbfile->object_type };
250 eval "require $class";
251 my $obj = $class->new(id => $dbfile->object_id)->load;
252 die 'no object found' unless $obj;
253 my $numberattr = $model_to_number{ $type_to_model{ $dbfile->object_type } };
254 return $obj->$numberattr;
258 # TODO not fully imlemented and tested
260 sub sync_all_locations {
261 my ($self, %params) = @_;
263 my %dateparms = (dateformat => 'yyyymmdd');
265 foreach my $type (keys %type_to_path) {
268 file_type => $params{file_type},
271 my @oldfiles = @{ SL::DB::Manager::File->get_all(
273 file_type => $params{file_type},
279 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id},$type_to_path{$type});
281 if (opendir my $dir, $path) {
282 foreach my $file (sort { lc $a cmp lc $b }
283 map { decode("UTF-8", $_) } readdir $dir)
285 next if (($file eq '.') || ($file eq '..'));
290 my ($filename, $number, $date, $time_ext) = split(/_/, $fname);
291 my ($time, $ext) = split(/\./, $time_ext, 2);
293 $time = substr($time, 0, 2) . ':' . substr($time, 2, 2) . ':' . substr($time, 4, 2);
295 #my @found = grep { $_->backend_data eq $fname } @oldfiles;
296 #if (scalar(@found) > 0) {
297 # @oldfiles = grep { $_ != @found[0] } @oldfiles;
300 my $dbfile = SL::DB::File->new();
301 my $class = 'SL::DB::Manager::' . $type_to_model{$type};
304 $model_to_number{ $type_to_model{$type} } => $number);
307 my $mime_type = File::MimeInfo::Magic::magic(File::Spec->catfile($path, $fname));
309 # if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix
310 $mime_type = File::MimeInfo::Magic::mimetype($fname);
311 $mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
314 $dbfile->assign_attributes(
315 object_id => $obj->id,
316 object_type => $type,
317 source => $params{file_type} eq 'document' ? 'created' : 'uploaded',
318 file_type => $params{file_type},
319 file_name => $filename . '_' . $number . '_' . $ext,
320 mime_type => $mime_type,
321 itime => $::locale->parse_date_to_object($date . ' ' . $time, %dateparms),
343 SL::File::Backend::Filesystem - Filesystem class for file storage backend
347 See the synopsis of L<SL::File::Backend>.
351 This specific storage backend use a Filesystem which is only accessed by this interface.
352 This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
353 This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
354 to store the files not to flat in the filesystem.
359 See methods of L<SL::File::Backend>.
367 The synchronization must be tested and a periodical task is needed to synchronize in some time periods.
371 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>