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 accounts_payable => 'kreditorenbuchungen',
134 my %type_to_model = (
135 sales_quotation => 'Order',
136 sales_order => 'Order',
137 request_quotation => 'Order',
138 purchase_order => 'Order',
139 sales_delivery_order => 'DeliveryOrder',
140 purchase_delivery_order => 'DeliveryOrder',
141 credit_note => 'Invoice',
142 invoice => 'Invoice',
143 purchase_invoice => 'PurchaseInvoice',
148 general_ledger => 'GLTransaction',
149 accounts_payable => 'GLTransaction',
152 my %model_to_number = (
153 Order => 'ordnumber',
154 DeliveryOrder => 'ordnumber',
155 Invoice => 'invnumber',
156 PurchaseInvoice => 'invnumber',
157 Part => 'partnumber',
158 Letter => 'letternumber',
159 GLTransaction => 'reference'
163 my ($self, $dbfile) = @_;
165 #die "No webdav backend enabled" unless $::instance_conf->get_webdav;
167 my $type = $type_to_path{ $dbfile->object_type };
169 die "Unknown type" unless $type;
171 my $number = $dbfile->backend_data;
173 $number = $self->_get_number_from_model($dbfile);
174 $dbfile->backend_data($number);
177 $main::lxdebug->message(LXDebug->DEBUG2(), "file_name=" . $dbfile->file_name ." number=".$number);
179 my @fileparts = split(/_/, $dbfile->file_name);
180 my $number_ext = pop @fileparts;
181 my ($maynumber, $ext) = split(/\./, $number_ext, 2);
182 push @fileparts, $maynumber if $maynumber ne $number;
184 my $basename = join('_', @fileparts);
186 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id}, $type, $number);
188 File::Path::make_path($path, { chmod => 0770 });
190 my $fname = $basename . '_' . $number . '_' . $dbfile->itime->strftime('%Y%m%d_%H%M%S');
191 $fname .= '.' . $ext if $ext;
193 $main::lxdebug->message(LXDebug->DEBUG2(), "webdav path=" . $path . " filename=" . $fname);
195 return (File::Spec->catfile($path, $fname), $path, $fname);
201 #TODO immer noch das alte Problem:
202 #je nachdem von woher der Aufruf kommt ist man in ./users oder .
203 my $rootdir = POSIX::getcwd();
204 my $basename = basename($rootdir);
205 my $dirname = dirname($rootdir);
206 $rootdir = $dirname if $basename eq 'users';
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>