1 package SL::File::Backend::Webdav;
5 use parent qw(SL::File::Backend);
8 use SL::System::Process;
13 use File::Path qw(make_path);
14 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;
79 my $dt = DateTime->from_epoch(epoch => $st[9])->clone();
80 $main::lxdebug->message(LXDebug->DEBUG2(), "dt=" .$dt);
85 my ($self, %params) = @_;
86 die "no dbfile" unless $params{dbfile};
87 my ($path, undef, undef) = $self->webdav_path($params{dbfile});
88 die "No file found in Backend: " . $path unless -f $path;
93 my ($self, %params) = @_;
94 my $path = $self->get_filepath(%params);
95 return "" unless $path;
96 my $contents = File::Slurp::read_file($path);
100 sub sync_from_backend {
101 my ($self, %params) = @_;
102 return unless $params{file_type};
104 $self->sync_all_locations(%params);
109 return $::instance_conf->get_doc_webdav;
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',
127 service => 'dienstleistungen',
128 assembly => 'erzeugnisse',
130 general_ledger => 'dialogbuchungen',
131 gl_transaction => 'dialogbuchungen',
132 accounts_payable => 'kreditorenbuchungen',
133 shop_image => 'shopbilder',
134 customer => 'kunden',
135 vendor => 'lieferanten',
138 my %type_to_model = (
139 sales_quotation => 'Order',
140 sales_order => 'Order',
141 request_quotation => 'Order',
142 purchase_order => 'Order',
143 sales_delivery_order => 'DeliveryOrder',
144 purchase_delivery_order => 'DeliveryOrder',
145 credit_note => 'Invoice',
146 invoice => 'Invoice',
147 purchase_invoice => 'PurchaseInvoice',
152 general_ledger => 'GLTransaction',
153 gl_transaction => 'GLTransaction',
154 accounts_payable => 'GLTransaction',
155 shop_image => 'Part',
156 customer => 'Customer',
160 my %model_to_number = (
161 Order => 'ordnumber',
162 DeliveryOrder => 'ordnumber',
163 Invoice => 'invnumber',
164 PurchaseInvoice => 'invnumber',
165 Part => 'partnumber',
166 Letter => 'letternumber',
167 GLTransaction => 'reference',
168 ShopImage => 'partnumber',
169 Customer => 'customernumber',
170 Vendor => 'vendornumber',
174 my ($self, $dbfile) = @_;
176 #die "No webdav backend enabled" unless $::instance_conf->get_webdav;
178 my $type = $type_to_path{ $dbfile->object_type };
180 die "Unknown type" unless $type;
182 my $number = $dbfile->backend_data;
184 $number = $self->_get_number_from_model($dbfile);
185 $dbfile->backend_data($number);
188 $main::lxdebug->message(LXDebug->DEBUG2(), "file_name=" . $dbfile->file_name ." number=".$number);
190 my @fileparts = split(/_/, $dbfile->file_name);
191 my $number_ext = pop @fileparts;
192 my ($maynumber, $ext) = split(/\./, $number_ext, 2);
193 push @fileparts, $maynumber if $maynumber ne $number;
195 my $basename = join('_', @fileparts);
197 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id}, $type, $number);
199 File::Path::make_path($path, { chmod => 0770 });
201 my $fname = $basename . '_' . $number . '_' . $dbfile->itime->strftime('%Y%m%d_%H%M%S');
202 $fname .= '.' . $ext if $ext;
204 $main::lxdebug->message(LXDebug->DEBUG2(), "webdav path=" . $path . " filename=" . $fname);
206 return (File::Spec->catfile($path, $fname), $path, $fname);
209 sub get_rootdir { SL::System::Process::exe_dir() }
211 sub _get_number_from_model {
212 my ($self, $dbfile) = @_;
214 my $class = 'SL::DB::' . $type_to_model{ $dbfile->object_type };
215 eval "require $class";
216 my $obj = $class->new(id => $dbfile->object_id)->load;
217 die 'no object found' unless $obj;
218 my $numberattr = $model_to_number{ $type_to_model{ $dbfile->object_type } };
219 return $obj->$numberattr;
223 # TODO not fully imlemented and tested
225 sub sync_all_locations {
226 my ($self, %params) = @_;
228 my %dateparms = (dateformat => 'yyyymmdd');
230 foreach my $type (keys %type_to_path) {
233 file_type => $params{file_type},
236 my @oldfiles = @{ SL::DB::Manager::File->get_all(
238 file_type => $params{file_type},
244 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id},$type_to_path{$type});
246 if (opendir my $dir, $path) {
247 foreach my $file (sort { lc $a cmp lc $b }
248 map { decode("UTF-8", $_) } readdir $dir)
250 next if (($file eq '.') || ($file eq '..'));
255 my ($filename, $number, $date, $time_ext) = split(/_/, $fname);
256 my ($time, $ext) = split(/\./, $time_ext, 2);
258 $time = substr($time, 0, 2) . ':' . substr($time, 2, 2) . ':' . substr($time, 4, 2);
260 #my @found = grep { $_->backend_data eq $fname } @oldfiles;
261 #if (scalar(@found) > 0) {
262 # @oldfiles = grep { $_ != @found[0] } @oldfiles;
265 my $dbfile = SL::DB::File->new();
266 my $class = 'SL::DB::Manager::' . $type_to_model{$type};
269 $model_to_number{ $type_to_model{$type} } => $number);
272 my $mime_type = File::MimeInfo::Magic::magic(File::Spec->catfile($path, $fname));
274 # if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix
275 $mime_type = File::MimeInfo::Magic::mimetype($fname);
276 $mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
279 $dbfile->assign_attributes(
280 object_id => $obj->id,
281 object_type => $type,
282 source => $params{file_type} eq 'document' ? 'created' : 'uploaded',
283 file_type => $params{file_type},
284 file_name => $filename . '_' . $number . '_' . $ext,
285 mime_type => $mime_type,
286 itime => $::locale->parse_date_to_object($date . ' ' . $time, %dateparms),
308 SL::File::Backend::Filesystem - Filesystem class for file storage backend
312 See the synopsis of L<SL::File::Backend>.
316 This specific storage backend use a Filesystem which is only accessed by this interface.
317 This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
318 This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
319 to store the files not to flat in the filesystem.
324 See methods of L<SL::File::Backend>.
332 The synchronization must be tested and a periodical task is needed to synchronize in some time periods.
336 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>