1 package SL::File::Backend::Webdav;
5 use parent qw(SL::File::Backend);
8 use SL::System::Process;
12 use File::Path qw(make_path);
13 use File::MimeInfo::Magic;
21 my ($self, %params) = @_;
22 $main::lxdebug->message(LXDebug->DEBUG2(), "del in backend " . $self . " file " . $params{dbfile});
23 $main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $params{dbfile}->id * 1);
24 return 0 unless $params{dbfile};
25 my ($file_path, undef, undef) = $self->webdav_path($params{dbfile});
31 my ($self, %params) = @_;
32 return 0 unless $params{dbfile};
33 my (undef, $oldwebdavname) = split(/:/, $params{dbfile}->location, 2);
34 my ($tofile, $basepath, $basename) = $self->webdav_path($params{dbfile});
35 my $fromfile = File::Spec->catfile($basepath, $oldwebdavname);
36 $main::lxdebug->message(LXDebug->DEBUG2(), "renamefrom=" . $fromfile . " to=" . $tofile);
37 move($fromfile, $tofile);
41 my ($self, %params) = @_;
42 die 'dbfile not exists' unless $params{dbfile};
43 $main::lxdebug->message(LXDebug->DEBUG2(), "in backend " . $self . " file " . $params{dbfile});
44 $main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $params{dbfile}->id);
45 my $dbfile = $params{dbfile};
46 die 'no file contents' unless $params{file_path} || $params{file_contents};
48 if ($params{dbfile}->id * 1 == 0) {
50 # new element: need id for file
51 $params{dbfile}->save;
53 my ($tofile, undef, $basename) = $self->webdav_path($params{dbfile});
54 if ($params{file_path} && -f $params{file_path}) {
55 copy($params{file_path}, $tofile);
57 elsif ($params{file_contents}) {
58 open(OUT, "> " . $tofile);
59 print OUT $params{file_contents};
65 sub get_version_count {
66 my ($self, %params) = @_;
67 die "no dbfile" unless $params{dbfile};
73 my ($self, %params) = @_;
74 die "no dbfile" unless $params{dbfile};
75 $main::lxdebug->message(LXDebug->DEBUG2(), "version=" .$params{version});
76 my ($path, undef, undef) = $self->webdav_path($params{dbfile});
77 die "No file found in Backend: " . $path unless -f $path;
78 my $dt = DateTime->from_epoch(epoch => stat($path)->mtime, time_zone => $::locale->get_local_time_zone()->name)->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 invoice_for_advance_payment => 'rechnungen',
125 final_invoice => 'rechnungen',
126 purchase_invoice => 'einkaufsrechnungen',
128 service => 'dienstleistungen',
129 assembly => 'erzeugnisse',
131 general_ledger => 'dialogbuchungen',
132 gl_transaction => 'dialogbuchungen',
133 accounts_payable => 'kreditorenbuchungen',
134 shop_image => 'shopbilder',
135 customer => 'kunden',
136 vendor => 'lieferanten',
139 my %type_to_model = (
140 sales_quotation => 'Order',
141 sales_order => 'Order',
142 request_quotation => 'Order',
143 purchase_order => 'Order',
144 sales_delivery_order => 'DeliveryOrder',
145 purchase_delivery_order => 'DeliveryOrder',
146 credit_note => 'Invoice',
147 invoice => 'Invoice',
148 invoice_for_advance_payment => 'Invoice',
149 final_invoice => 'Invoice',
150 purchase_invoice => 'PurchaseInvoice',
155 general_ledger => 'GLTransaction',
156 gl_transaction => 'GLTransaction',
157 accounts_payable => 'GLTransaction',
158 shop_image => 'Part',
159 customer => 'Customer',
163 my %model_to_number = (
164 Order => 'ordnumber',
165 DeliveryOrder => 'ordnumber',
166 Invoice => 'invnumber',
167 PurchaseInvoice => 'invnumber',
168 Part => 'partnumber',
169 Letter => 'letternumber',
170 GLTransaction => 'reference',
171 ShopImage => 'partnumber',
172 Customer => 'customernumber',
173 Vendor => 'vendornumber',
177 my ($self, $dbfile) = @_;
179 #die "No webdav backend enabled" unless $::instance_conf->get_webdav;
181 my $type = $type_to_path{ $dbfile->object_type };
183 die "Unknown type" unless $type;
185 my $number = $dbfile->backend_data;
187 $number = $self->_get_number_from_model($dbfile);
188 $dbfile->backend_data($number);
191 $main::lxdebug->message(LXDebug->DEBUG2(), "file_name=" . $dbfile->file_name ." number=".$number);
193 my @fileparts = split(/_/, $dbfile->file_name);
194 my $number_ext = pop @fileparts;
195 my ($maynumber, $ext) = split(/\./, $number_ext, 2);
196 push @fileparts, $maynumber if $maynumber ne $number;
198 my $basename = join('_', @fileparts);
200 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id}, $type, $number);
202 File::Path::make_path($path, { chmod => 0770 });
204 my $fname = $basename . '_' . $number . '_' . $dbfile->itime->strftime('%Y%m%d_%H%M%S');
205 $fname .= '.' . $ext if $ext;
207 $main::lxdebug->message(LXDebug->DEBUG2(), "webdav path=" . $path . " filename=" . $fname);
209 return (File::Spec->catfile($path, $fname), $path, $fname);
212 sub get_rootdir { SL::System::Process::exe_dir() }
214 sub _get_number_from_model {
215 my ($self, $dbfile) = @_;
217 my $class = 'SL::DB::' . $type_to_model{ $dbfile->object_type };
218 eval "require $class";
219 my $obj = $class->new(id => $dbfile->object_id)->load;
220 die 'no object found' unless $obj;
221 my $numberattr = $model_to_number{ $type_to_model{ $dbfile->object_type } };
222 return $obj->$numberattr;
226 # TODO not fully imlemented and tested
228 sub sync_all_locations {
229 my ($self, %params) = @_;
231 my %dateparms = (dateformat => 'yyyymmdd');
233 foreach my $type (keys %type_to_path) {
236 file_type => $params{file_type},
239 my @oldfiles = @{ SL::DB::Manager::File->get_all(
241 file_type => $params{file_type},
247 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id},$type_to_path{$type});
249 if (opendir my $dir, $path) {
250 foreach my $file (sort { lc $a cmp lc $b }
251 map { decode("UTF-8", $_) } readdir $dir)
253 next if (($file eq '.') || ($file eq '..'));
258 my ($filename, $number, $date, $time_ext) = split(/_/, $fname);
259 my ($time, $ext) = split(/\./, $time_ext, 2);
261 $time = substr($time, 0, 2) . ':' . substr($time, 2, 2) . ':' . substr($time, 4, 2);
263 #my @found = grep { $_->backend_data eq $fname } @oldfiles;
264 #if (scalar(@found) > 0) {
265 # @oldfiles = grep { $_ != @found[0] } @oldfiles;
268 my $dbfile = SL::DB::File->new();
269 my $class = 'SL::DB::Manager::' . $type_to_model{$type};
272 $model_to_number{ $type_to_model{$type} } => $number);
275 my $mime_type = File::MimeInfo::Magic::magic(File::Spec->catfile($path, $fname));
277 # if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix
278 $mime_type = File::MimeInfo::Magic::mimetype($fname);
279 $mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
282 $dbfile->assign_attributes(
283 object_id => $obj->id,
284 object_type => $type,
285 source => $params{file_type} eq 'document' ? 'created' : 'uploaded',
286 file_type => $params{file_type},
287 file_name => $filename . '_' . $number . '_' . $ext,
288 mime_type => $mime_type,
289 itime => $::locale->parse_date_to_object($date . ' ' . $time, %dateparms),
311 SL::File::Backend::Filesystem - Filesystem class for file storage backend
315 See the synopsis of L<SL::File::Backend>.
319 This specific storage backend use a Filesystem which is only accessed by this interface.
320 This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
321 This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
322 to store the files not to flat in the filesystem.
327 See methods of L<SL::File::Backend>.
335 The synchronization must be tested and a periodical task is needed to synchronize in some time periods.
339 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>