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