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',
136 my %type_to_model = (
137 sales_quotation => 'Order',
138 sales_order => 'Order',
139 request_quotation => 'Order',
140 purchase_order => 'Order',
141 sales_delivery_order => 'DeliveryOrder',
142 purchase_delivery_order => 'DeliveryOrder',
143 credit_note => 'Invoice',
144 invoice => 'Invoice',
145 purchase_invoice => 'PurchaseInvoice',
150 general_ledger => 'GLTransaction',
151 gl_transaction => 'GLTransaction',
152 accounts_payable => 'GLTransaction',
153 shop_image => 'Part',
156 my %model_to_number = (
157 Order => 'ordnumber',
158 DeliveryOrder => 'ordnumber',
159 Invoice => 'invnumber',
160 PurchaseInvoice => 'invnumber',
161 Part => 'partnumber',
162 Letter => 'letternumber',
163 GLTransaction => 'reference',
164 ShopImage => 'partnumber',
168 my ($self, $dbfile) = @_;
170 #die "No webdav backend enabled" unless $::instance_conf->get_webdav;
172 my $type = $type_to_path{ $dbfile->object_type };
174 die "Unknown type" unless $type;
176 my $number = $dbfile->backend_data;
178 $number = $self->_get_number_from_model($dbfile);
179 $dbfile->backend_data($number);
182 $main::lxdebug->message(LXDebug->DEBUG2(), "file_name=" . $dbfile->file_name ." number=".$number);
184 my @fileparts = split(/_/, $dbfile->file_name);
185 my $number_ext = pop @fileparts;
186 my ($maynumber, $ext) = split(/\./, $number_ext, 2);
187 push @fileparts, $maynumber if $maynumber ne $number;
189 my $basename = join('_', @fileparts);
191 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id}, $type, $number);
193 File::Path::make_path($path, { chmod => 0770 });
195 my $fname = $basename . '_' . $number . '_' . $dbfile->itime->strftime('%Y%m%d_%H%M%S');
196 $fname .= '.' . $ext if $ext;
198 $main::lxdebug->message(LXDebug->DEBUG2(), "webdav path=" . $path . " filename=" . $fname);
200 return (File::Spec->catfile($path, $fname), $path, $fname);
206 #TODO immer noch das alte Problem:
207 #je nachdem von woher der Aufruf kommt ist man in ./users oder .
208 my $rootdir = POSIX::getcwd();
209 my $basename = basename($rootdir);
210 my $dirname = dirname($rootdir);
211 $rootdir = $dirname if $basename eq 'users';
215 sub _get_number_from_model {
216 my ($self, $dbfile) = @_;
218 my $class = 'SL::DB::' . $type_to_model{ $dbfile->object_type };
219 eval "require $class";
220 my $obj = $class->new(id => $dbfile->object_id)->load;
221 die 'no object found' unless $obj;
222 my $numberattr = $model_to_number{ $type_to_model{ $dbfile->object_type } };
223 return $obj->$numberattr;
227 # TODO not fully imlemented and tested
229 sub sync_all_locations {
230 my ($self, %params) = @_;
232 my %dateparms = (dateformat => 'yyyymmdd');
234 foreach my $type (keys %type_to_path) {
237 file_type => $params{file_type},
240 my @oldfiles = @{ SL::DB::Manager::File->get_all(
242 file_type => $params{file_type},
248 my $path = File::Spec->catdir($self->get_rootdir, "webdav", $::auth->client->{id},$type_to_path{$type});
250 if (opendir my $dir, $path) {
251 foreach my $file (sort { lc $a cmp lc $b }
252 map { decode("UTF-8", $_) } readdir $dir)
254 next if (($file eq '.') || ($file eq '..'));
259 my ($filename, $number, $date, $time_ext) = split(/_/, $fname);
260 my ($time, $ext) = split(/\./, $time_ext, 2);
262 $time = substr($time, 0, 2) . ':' . substr($time, 2, 2) . ':' . substr($time, 4, 2);
264 #my @found = grep { $_->backend_data eq $fname } @oldfiles;
265 #if (scalar(@found) > 0) {
266 # @oldfiles = grep { $_ != @found[0] } @oldfiles;
269 my $dbfile = SL::DB::File->new();
270 my $class = 'SL::DB::Manager::' . $type_to_model{$type};
273 $model_to_number{ $type_to_model{$type} } => $number);
276 my $mime_type = File::MimeInfo::Magic::magic(File::Spec->catfile($path, $fname));
278 # if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix
279 $mime_type = File::MimeInfo::Magic::mimetype($fname);
280 $mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
283 $dbfile->assign_attributes(
284 object_id => $obj->id,
285 object_type => $type,
286 source => $params{file_type} eq 'document' ? 'created' : 'uploaded',
287 file_type => $params{file_type},
288 file_name => $filename . '_' . $number . '_' . $ext,
289 mime_type => $mime_type,
290 itime => $::locale->parse_date_to_object($date . ' ' . $time, %dateparms),
312 SL::File::Backend::Filesystem - Filesystem class for file storage backend
316 See the synopsis of L<SL::File::Backend>.
320 This specific storage backend use a Filesystem which is only accessed by this interface.
321 This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
322 This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
323 to store the files not to flat in the filesystem.
328 See methods of L<SL::File::Backend>.
336 The synchronization must be tested and a periodical task is needed to synchronize in some time periods.
340 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>