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" if !-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" if !-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 0 unless $::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);
212 #TODO immer noch das alte Problem:
213 #je nachdem von woher der Aufruf kommt ist man in ./users oder .
214 my $rootdir = POSIX::getcwd();
215 my $basename = basename($rootdir);
216 my $dirname = dirname($rootdir);
217 $rootdir = $dirname if $basename eq 'users';
221 sub _get_number_from_model {
222 my ($self, $dbfile) = @_;
224 my $class = 'SL::DB::' . $type_to_model{ $dbfile->object_type };
225 eval "require $class";
226 my $obj = $class->new(id => $dbfile->object_id)->load;
227 die 'no object found' unless $obj;
228 my $numberattr = $model_to_number{ $type_to_model{ $dbfile->object_type } };
229 return $obj->$numberattr;
233 # TODO not fully imlemented and tested
235 sub sync_all_locations {
236 my ($self, %params) = @_;
238 my %dateparms = (dateformat => 'yyyymmdd');
240 foreach my $type (keys %type_to_path) {
243 file_type => $params{file_type},
246 my @oldfiles = @{ SL::DB::Manager::File->get_all(
248 file_type => $params{file_type},
254 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id},$type_to_path{$type});
256 if (opendir my $dir, $path) {
257 foreach my $file (sort { lc $a cmp lc $b }
258 map { decode("UTF-8", $_) } readdir $dir)
260 next if (($file eq '.') || ($file eq '..'));
265 my ($filename, $number, $date, $time_ext) = split(/_/, $fname);
266 my ($time, $ext) = split(/\./, $time_ext, 2);
268 $time = substr($time, 0, 2) . ':' . substr($time, 2, 2) . ':' . substr($time, 4, 2);
270 #my @found = grep { $_->backend_data eq $fname } @oldfiles;
271 #if (scalar(@found) > 0) {
272 # @oldfiles = grep { $_ != @found[0] } @oldfiles;
275 my $dbfile = SL::DB::File->new();
276 my $class = 'SL::DB::Manager::' . $type_to_model{$type};
279 $model_to_number{ $type_to_model{$type} } => $number);
282 my $mime_type = File::MimeInfo::Magic::magic(File::Spec->catfile($path, $fname));
284 # if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix
285 $mime_type = File::MimeInfo::Magic::mimetype($fname);
286 $mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
289 $dbfile->assign_attributes(
290 object_id => $obj->id,
291 object_type => $type,
292 source => $params{file_type} eq 'document' ? 'created' : 'uploaded',
293 file_type => $params{file_type},
294 file_name => $filename . '_' . $number . '_' . $ext,
295 mime_type => $mime_type,
296 itime => $::locale->parse_date_to_object($date . ' ' . $time, %dateparms),
318 SL::File::Backend::Filesystem - Filesystem class for file storage backend
322 See the synopsis of L<SL::File::Backend>.
326 This specific storage backend use a Filesystem which is only accessed by this interface.
327 This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
328 This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
329 to store the files not to flat in the filesystem.
334 See methods of L<SL::File::Backend>.
342 The synchronization must be tested and a periodical task is needed to synchronize in some time periods.
346 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>