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 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 @fileparts = split(/_/, $dbfile->file_name);
190 my $number_ext = pop @fileparts;
191 my ($maynumber, $ext) = split(/\./, $number_ext, 2);
192 push @fileparts, $maynumber if $maynumber ne $number;
194 my $basename = join('_', @fileparts);
196 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id}, $type, $number);
198 File::Path::make_path($path, { chmod => 0770 });
200 my $fname = $basename . '_' . $number . '_' . $dbfile->itime->strftime('%Y%m%d_%H%M%S');
201 $fname .= '.' . $ext if $ext;
203 $main::lxdebug->message(LXDebug->DEBUG2(), "webdav path=" . $path . " filename=" . $fname);
205 return (File::Spec->catfile($path, $fname), $path, $fname);
208 sub get_rootdir { SL::System::Process::exe_dir() }
210 sub _get_number_from_model {
211 my ($self, $dbfile) = @_;
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;
222 # TODO not fully imlemented and tested
224 sub sync_all_locations {
225 my ($self, %params) = @_;
227 my %dateparms = (dateformat => 'yyyymmdd');
229 foreach my $type (keys %type_to_path) {
232 file_type => $params{file_type},
235 my @oldfiles = @{ SL::DB::Manager::File->get_all(
237 file_type => $params{file_type},
243 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id},$type_to_path{$type});
245 if (opendir my $dir, $path) {
246 foreach my $file (sort { lc $a cmp lc $b }
247 map { decode("UTF-8", $_) } readdir $dir)
249 next if (($file eq '.') || ($file eq '..'));
254 my ($filename, $number, $date, $time_ext) = split(/_/, $fname);
255 my ($time, $ext) = split(/\./, $time_ext, 2);
257 $time = substr($time, 0, 2) . ':' . substr($time, 2, 2) . ':' . substr($time, 4, 2);
259 #my @found = grep { $_->backend_data eq $fname } @oldfiles;
260 #if (scalar(@found) > 0) {
261 # @oldfiles = grep { $_ != @found[0] } @oldfiles;
264 my $dbfile = SL::DB::File->new();
265 my $class = 'SL::DB::Manager::' . $type_to_model{$type};
268 $model_to_number{ $type_to_model{$type} } => $number);
271 my $mime_type = File::MimeInfo::Magic::magic(File::Spec->catfile($path, $fname));
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;
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),
307 SL::File::Backend::Filesystem - Filesystem class for file storage backend
311 See the synopsis of L<SL::File::Backend>.
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.
323 See methods of L<SL::File::Backend>.
331 The synchronization must be tested and a periodical task is needed to synchronize in some time periods.
335 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>