Filemanagement: Speichern der erzeugten Mahnungsdokumente
[kivitendo-erp.git] / SL / Controller / File.pm
1 package SL::Controller::File;
2
3 use strict;
4
5 use parent qw(SL::Controller::Base);
6
7 use List::Util qw(first max);
8
9 use utf8;
10 use Encode qw(decode);
11 use URI::Escape;
12 use Cwd;
13 use DateTime;
14 use File::stat;
15 use File::Spec::Unix;
16 use File::Spec::Win32;
17 use File::MimeInfo::Magic;
18 use SL::DB::Helper::Mappings;
19 use SL::DB::Order;
20 use SL::DB::DeliveryOrder;
21 use SL::DB::Invoice;
22
23 use SL::DB::PurchaseInvoice;
24 use SL::DB::Part;
25 use SL::DB::GLTransaction;
26 use SL::DB::Draft;
27 use SL::DB::History;
28 use SL::JSON;
29 use SL::Helper::CreatePDF qw(:all);
30 use SL::Locale::String;
31 use SL::SessionFile;
32 use SL::File;
33 use SL::Controller::Helper::ThumbnailCreator qw(file_probe_image_type);
34
35 use constant DO_DELETE    =>   0;
36 use constant DO_UNIMPORT  =>   1;
37
38
39 use Rose::Object::MakeMethods::Generic
40 (
41     'scalar --get_set_init' => [ qw() ],
42     'scalar' => [ qw(object object_type object_model object_id object_right file_type files is_global existing) ],
43 );
44
45 __PACKAGE__->run_before('check_object_params', only => [ qw(list ajax_delete ajax_importdialog ajax_import ajax_unimport ajax_upload ajax_files_uploaded) ]);
46
47 my %file_types = (
48   'sales_quotation' =>         { gen => 1 ,gltype => '',   dir =>'SalesQuotation',       model => 'Order',          right => 'import_ar'  },
49   'sales_order'     =>         { gen => 1 ,gltype => '',   dir =>'SalesOrder',           model => 'Order',          right => 'import_ar'  },
50   'sales_delivery_order' =>    { gen => 1 ,gltype => '',   dir =>'SalesDeliveryOrder',   model => 'DeliveryOrder',  right => 'import_ar'  },
51   'invoice'         =>         { gen => 1 ,gltype => 'ar', dir =>'SalesInvoice',         model => 'Invoice',        right => 'import_ar'  },
52   'credit_note'     =>         { gen => 1 ,gltype => '',   dir =>'CreditNote',           model => 'Invoice',        right => 'import_ar'  },
53   'request_quotation' =>       { gen => 3 ,gltype => '',   dir =>'RequestForQuotation',  model => 'Order',          right => 'import_ap'  },
54   'purchase_order' =>          { gen => 3 ,gltype => '',   dir =>'PurchaseOrder',        model => 'Order',          right => 'import_ap'  },
55   'purchase_delivery_order' => { gen => 3 ,gltype => '',   dir =>'PurchaseDeliveryOrder',model => 'DeliveryOrder',  right => 'import_ap'  },
56   'purchase_invoice' =>        { gen => 2 ,gltype => 'ap', dir =>'PurchaseInvoice',      model => 'PurchaseInvoice',right => 'import_ap'  },
57   'vendor' =>                  { gen => 0 ,gltype => '',   dir =>'Vendor',               model => 'Vendor',         right => 'xx'  },
58   'customer' =>                { gen => 1 ,gltype => '',   dir =>'Customer',             model => 'Customer',       right => 'xx'  },
59   'part' =>                    { gen => 0 ,gltype => '',   dir =>'Part',                 model => 'Part',           right => 'xx'  },
60   'gl_transaction' =>          { gen => 2 ,gltype => 'gl', dir =>'GeneralLedger',        model => 'GLTransaction',  right => 'import_ap'  },
61   'draft' =>                   { gen => 0 ,gltype => '',   dir =>'Draft',                model => 'Draft',          right => 'xx'  },
62   'csv_customer' =>            { gen => 1 ,gltype => '',   dir =>'Reports',              model => 'Customer',       right => 'xx'  },
63   'csv_vendor'   =>            { gen => 1 ,gltype => '',   dir =>'Reports',              model => 'Vendor',         right => 'xx'  },
64 );
65
66 #--- 4 locale ---#
67 # $main::locale->text('imported')
68
69 #
70 # actions
71 #
72
73 sub action_list {
74   my ($self) = @_;
75
76   my $isjson = 0;
77   $isjson = 1 if $::form->{json};
78
79   $self->_do_list($isjson);
80 }
81
82 sub action_ajax_importdialog {
83   my ($self) = @_;
84   $::auth->assert($self->object_right);
85   my $path   = $::form->{path};
86   my @files  = $self->_get_from_import($path);
87   my $source = {
88     'name'         => $::form->{source},
89     'path'         => $path ,
90     'chk_action'   => $::form->{source}.'_import',
91     'chk_title'    => $main::locale->text('Import scanned documents'),
92     'chkall_title' => $main::locale->text('Import all'),
93     'files'        => \@files
94   };
95   $self->render('file/import_dialog',
96                 { layout => 0
97                 },
98                 source => $source
99   );
100 }
101
102 sub action_ajax_import {
103   my ($self) = @_;
104   $::auth->assert($self->object_right);
105   my $ids    = $::form->{ids};
106   my $source = $::form->{source};
107   my $path   = $::form->{path};
108   my @files = $self->_get_from_import($path);
109   foreach my $filename (@{ $::form->{$ids} || [] }) {
110     my ($file,undef) = grep { $_->{name} eq $filename } @files;
111     if ( $file ) {
112       my $obj = SL::File->save(object_id     => $self->object_id,
113                                object_type   => $self->object_type,
114                                mime_type     => 'application/pdf',
115                                source        => $source,
116                                file_type     => 'document',
117                                file_name     => $file->{filename},
118                                file_path     => $file->{path}
119                              );
120       unlink($file->{path}) if $obj;
121     }
122   }
123   $self->_do_list(1);
124 }
125
126 sub action_ajax_delete {
127   my ($self) = @_;
128   $self->_delete_all(DO_DELETE,$::locale->text('Following files are deleted:'));
129 }
130
131 sub action_ajax_unimport {
132   my ($self) = @_;
133   $self->_delete_all(DO_UNIMPORT,$::locale->text('Following files are unimported:'));
134 }
135
136 sub action_ajax_rename {
137   my ($self) = @_;
138   my $file = SL::File->get(id => $::form->{id});
139   if ( ! $file ) {
140     $self->js->flash('error',$::locale->text('File not exists !'))->render();
141     return;
142   }
143   $main::lxdebug->message(LXDebug->DEBUG2(), "object_id=".$file->object_id." object_type=".$file->object_type." dbfile=".$file);
144   my $sessionfile = $::form->{sessionfile};
145       $main::lxdebug->message(LXDebug->DEBUG2(), "sessionfile=".$sessionfile);
146   if ( $sessionfile && -f $sessionfile ) {
147      $main::lxdebug->message(LXDebug->DEBUG2(), "file=".$file->file_name." to=".$::form->{to}." sessionfile=".$sessionfile);
148     # new uploaded file
149     if ( $::form->{to} eq $file->file_name ) {
150       # no rename so use as new version
151       $file->save_file($sessionfile);
152       $self->js->flash('warning',$::locale->text('File \'#1\' is used as new Version !',$file->file_name));
153
154     } else {
155       # new filename so it is a new file with same attributes as old file
156       eval {
157         SL::File->save(object_id     => $file->object_id,
158                        object_type   => $file->object_type,
159                        mime_type     => $file->mime_type,
160                        source        => $file->source,
161                        file_type     => $file->file_type,
162                        file_name     => $::form->{to},
163                        file_path     => $sessionfile
164                      );
165         unlink($sessionfile);
166         1;
167       } or do {
168         $self->js->flash(       'error', t8('internal error (see details)'))
169                  ->flash_detail('error', $@)->render;
170         return;
171       }
172     }
173
174   } else {
175     # normal rename
176     my $res;
177
178     eval {
179       $res = $file->rename($::form->{to});
180       $main::lxdebug->message(LXDebug->DEBUG2(), "rename result=".$res);
181       1;
182     } or do {
183       $self->js->flash(       'error', t8('internal error (see details)'))
184                ->flash_detail('error', $@)->render;
185       return;
186     };
187
188     if ($res != SL::File::RENAME_OK) {
189       $self->js->flash('error',
190                          $res == SL::File::RENAME_EXISTS ? $::locale->text('File still exists !')
191                        : $res == SL::File::RENAME_SAME   ? $::locale->text('Same Filename !')
192                        :                                   $::locale->text('File not exists !'))
193         ->render;
194       return;
195     }
196   }
197   $self->is_global($::form->{is_global});
198   $self->file_type(  $file->file_type);
199   $self->object_type($file->object_type);
200   $self->object_id(  $file->object_id);
201   #$self->object_model($file_types{$file->module}->{model});
202   #$self->object_right($file_types{$file->module}->{right});
203   if ( $::form->{next_ids} ) {
204     my @existing = split(/,/, $::form->{next_ids});
205     $self->existing(\@existing);
206   }
207   $self->_do_list(1);
208 }
209
210 sub action_ajax_upload {
211   my ($self) = @_;
212   $self->{maxsize} = $::instance_conf->get_doc_max_filesize;
213   $self->{accept_types} = '';
214   $self->{accept_types} = 'image/png,image/gif,image/jpeg,image/tiff,*png,*gif,*.jpg,*.tif' if $self->{file_type} eq 'image';
215   $self->render('file/upload_dialog',
216                 { layout          => 0
217                 },
218   );
219 }
220
221 sub action_ajax_files_uploaded {
222   my ($self) = @_;
223
224   my $source = 'uploaded';
225   $main::lxdebug->message(LXDebug->DEBUG2(), "file_upload UPLOAD=".$::form->{ATTACHMENTS}->{uploadfiles});
226   my @existing;
227   if ( $::form->{ATTACHMENTS}->{uploadfiles} ) {
228     my @upfiles = @{ $::form->{ATTACHMENTS}->{uploadfiles} };
229     foreach my $idx (0 .. scalar(@upfiles) - 1) {
230       eval {
231         my $fname = uri_unescape($upfiles[$idx]->{filename});
232         $main::lxdebug->message(LXDebug->DEBUG2(), "file_upload name=".$fname);
233         ## normalize and find basename
234         # first split with unix rules
235         # after that split with windows rules
236         my ($volume,$directories,$basefile) = File::Spec::Unix->splitpath($fname);
237         ($volume,$directories,$basefile) = File::Spec::Win32->splitpath($basefile);
238
239         # to find real mime_type by magic we must save the filedata
240
241         my $sess_fname = "file_upload_".$self->object_type."_".$self->object_id."_".$idx;
242         my $sfile     = SL::SessionFile->new($sess_fname, mode => 'w');
243
244         $sfile->fh->print(${$upfiles[$idx]->{data}});
245         $sfile->fh->close;
246         my $mime_type = File::MimeInfo::Magic::magic($sfile->file_name);
247
248         if (! $mime_type) {
249           # if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix
250           $mime_type = File::MimeInfo::Magic::mimetype($basefile);
251           $mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
252         }
253         $main::lxdebug->message(LXDebug->DEBUG2(), "mime_type=".$mime_type);
254         if ( $self->file_type eq 'image' && $self->file_probe_image_type($mime_type, $basefile)) {
255           next;
256         }
257         my ($existobj) = SL::File->get_all(object_id     => $self->object_id,
258                                         object_type   => $self->object_type,
259                                         mime_type     => $mime_type,
260                                         source        => $source,
261                                         file_type     => $self->file_type,
262                                         file_name     => $basefile,
263                                       );
264
265         $main::lxdebug->message(LXDebug->DEBUG2(), "store1 exist=".$existobj);
266         if ($existobj) {
267   $main::lxdebug->message(LXDebug->DEBUG2(), "id=".$existobj->id." sessionfile=". $sfile->file_name);
268           push @existing, $existobj->id.'_'.$sfile->file_name;
269         } else {
270           my $fileobj = SL::File->save(object_id     => $self->object_id,
271                                        object_type   => $self->object_type,
272                                        mime_type     => $mime_type,
273                                        source        => $source,
274                                        file_type     => $self->file_type,
275                                        file_name     => $basefile,
276                                        ## two possibilities: what is better ? content or sessionfile ??
277                                        #file_contents => ${$upfiles[$idx]->{data}},
278                                        file_path     => $sfile->file_name
279                                      );
280           $main::lxdebug->message(LXDebug->DEBUG2(), "obj=".$fileobj);
281           unlink($sfile->file_name);
282         }
283         1;
284       } or do {
285         $self->js->flash(       'error', t8('internal error (see details)'))
286                  ->flash_detail('error', $@)->render;
287         return;
288       }
289     }
290   }
291   $self->existing(\@existing);
292   $self->_do_list(1);
293 }
294
295 sub action_download {
296   my ($self) = @_;
297   my ($id,$version) = split /_/, $::form->{id};
298   my $file = SL::File->get(id => $id );
299   $file->version($version) if $version;
300   my $ref  = $file->get_content;
301   if ( $file && $ref ) {
302     return $self->send_file($ref,
303       type => $file->mime_type,
304       name => $file->file_name,
305     );
306   }
307 }
308
309 #
310 # filters
311 #
312
313 sub check_object_params {
314   my ($self) = @_;
315
316   my $id = ($::form->{object_id} // 0) * 1;
317   my $draftid = ($::form->{draft_id} // 0) * 1;
318   my $gldoc = 0;
319   my $type = undef;
320
321   if ( $draftid == 0 && $id == 0 && $::form->{is_global} ) {
322     $gldoc = 1;
323     $type = $::form->{object_type};
324   }
325   elsif ( $id == 0 ) {
326     $id = $::form->{draft_id};
327     $type = 'draft';
328   } elsif ( $::form->{object_type} ) {
329     $type = $::form->{object_type};
330   }
331   die "No object type"     if ! $type;
332   die "No file type"       if ! $::form->{file_type};
333   die "Unkown object type" if ! $file_types{$type};
334
335   $self->is_global($gldoc);
336   $self->file_type($::form->{file_type});
337   $self->object_type($type);
338   $self->object_id($id);
339   $self->object_model($file_types{$type}->{model});
340   $self->object_right($file_types{$type}->{right});
341   $main::lxdebug->message(LXDebug->DEBUG2(), "checked: object_id=".$self->object_id." object_type=".$self->object_type." is_global=".$self->is_global);
342
343  # $::auth->assert($self->object_right);
344
345  # my $model = 'SL::DB::' . $self->object_model;
346  # $self->object($model->new(id => $self->object_id)->load || die "Record not found");
347
348   return 1;
349 }
350
351 #
352 # private methods
353 #
354
355 sub _delete_all {
356   my ($self,$do_unimport,$infotext) = @_;
357   my $files = '';
358   my $ids = $::form->{ids};
359   foreach my $id_version (@{ $::form->{$ids} || [] }) {
360     my ($id,$version) = split /_/, $id_version;
361     my $dbfile = SL::File->get(id => $id);
362     $dbfile->version($version) if $dbfile && $version;
363     if ( $dbfile && $dbfile->delete ) {
364       $files .= ' '.$dbfile->file_name;
365     }
366   }
367   $self->js->flash('info',$infotext.$files) if $files;
368   $self->_do_list(1);
369 }
370
371 sub _do_list {
372   my ($self,$json) = @_;
373   my @files;
374   $main::lxdebug->message(LXDebug->DEBUG2(), "do_list: object_id=".$self->object_id." object_type=".$self->object_type." file_type=".$self->file_type." json=".$json);
375   if ( $self->file_type eq 'document' ) {
376     my @object_types;
377     push @object_types, $self->object_type;
378     push @object_types, ('dunning','dunning1','dunning2','dunning3') if $self->object_type eq 'invoice';
379     @files   = SL::File->get_all_versions(object_id   => $self->object_id  ,
380                                           object_type => \@object_types,
381                                           file_type   => $self->file_type  );
382
383     $main::lxdebug->message(LXDebug->DEBUG2(), "cnt1=".scalar(@files));
384   }
385   elsif ( $self->file_type eq 'attachment' ||  $self->file_type eq 'image' ) {
386     @files   = SL::File->get_all(object_id   => $self->object_id  ,
387                                  object_type => $self->object_type,
388                                  file_type   => $self->file_type  );
389     $main::lxdebug->message(LXDebug->DEBUG2(), "cnt2=".scalar(@files));
390   }
391   $self->files(\@files);
392   $self->_mk_render('file/list',1,0,$json);
393 }
394
395 sub _get_from_import {
396   my ($self,$path) = @_;
397   my @foundfiles ;
398
399   $main::lxdebug->message(LXDebug->DEBUG2(), "import path=".$path);
400   my $language = $::lx_office_conf{system}->{language};
401   my $timezone = $::locale->get_local_time_zone()->name;
402   if (opendir my $dir, $path) {
403     my @files = ( readdir $dir);
404     foreach my $file ( @files) {
405       next if (($file eq '.') || ($file eq '..'));
406       $file = Encode::decode('utf-8', $file);
407       $main::lxdebug->message(LXDebug->DEBUG2(), "file=".$file);
408
409       next if( -d "$path/$file");
410
411       my $tmppath = File::Spec->catfile( $path, $file );
412       $main::lxdebug->message(LXDebug->DEBUG2(), "tmppath=".$tmppath." file=".$file);
413       next if( ! -f $tmppath);
414
415       my $st = stat($tmppath);
416       my $dt = DateTime->from_epoch( epoch => $st->mtime, time_zone => $timezone, locale => $language);
417       my $sname = $main::locale->quote_special_chars('HTML',$file);
418       push @foundfiles , {
419         'name'     => $file,
420         'filename' => $sname,
421         'path'     => $tmppath,
422         'mtime'    => $st->mtime,
423         'date'     => $dt->dmy('.')." ".$dt->hms,
424       };
425
426     }
427   }
428   $main::lxdebug->message(LXDebug->DEBUG2(), "return ".scalar(@foundfiles)." files");
429   return @foundfiles;
430 }
431
432 sub _mk_render {
433   my ($self,$template,$edit,$scanner,$json) = @_;
434   my $err;
435   eval {
436     ##TODO  here a configurable code must be implemented
437
438     my $title;
439     $main::lxdebug->message(LXDebug->DEBUG2(), "mk_render: object_id=".$self->object_id." object_type=".$self->object_type.
440                               " file_type=".$self->file_type." json=".$json." filecount=".scalar(@{ $self->files })." is_global=".$self->is_global);
441     my @sources = $self->_get_sources();
442     foreach my $source ( @sources ) {
443       $main::lxdebug->message(LXDebug->DEBUG2(), "mk_render: source name=".$source->{name});
444       @{$source->{files}} = grep { $_->source eq $source->{name}} @{ $self->files };
445     }
446     if ( $self->file_type eq 'document' ) {
447       $title = $main::locale->text('Documents');
448     } elsif ( $self->file_type eq 'attachment' ) {
449       $title = $main::locale->text('Attachments');
450     } elsif ( $self->file_type eq 'image' ) {
451       $title = $main::locale->text('Images');
452     }
453
454     my $output         = SL::Presenter->get->render(
455       $template,
456       title             => $title,
457       SOURCES           => \@sources,
458       edit_attachments  => $edit,
459       object_type       => $self->object_type,
460       object_id         => $self->object_id,
461       file_type         => $self->file_type,
462       is_global         => $self->is_global,
463       json              => $json,
464     );
465     if ( $json ) {
466       $self->js->html('#'.$self->file_type.'_list_'.$self->object_type, $output);
467       if ( $self->existing && scalar(@{$self->existing}) > 0) {
468         my $first = shift @{$self->existing};
469         my ($first_id,$sfile) = split('_',$first,2);
470         #$main::lxdebug->message(LXDebug->DEBUG2(), "id=".$first_id." sessionfile=". $sfile);
471         my $file = SL::File->get(id => $first_id );
472         $self->js->run('kivi.File.askForRename',$first_id,$file->file_name,$sfile,join (',', @{$self->existing}), $self->is_global);
473       }
474       $self->js->render();
475     } else {
476         $self->render(\$output, { layout => 0, process => 0 });
477     }
478     1;
479   } or do {
480     if ($json ){
481       $self->js->flash(       'error', t8('internal error (see details)'))
482                ->flash_detail('error', $@)->render;
483     } else {
484       $self->render('generic/error', { layout => 0 }, label_error => $@);
485     }
486   };
487 }
488
489
490 sub _get_sources {
491   my ($self) = @_;
492   my @sources;
493   $main::lxdebug->message(LXDebug->DEBUG2(), "get_sources file_type=". $self->file_type);
494   if ( $self->file_type eq 'document' ) {
495     ##TODO statt gen neue attribute in filetypes :
496     if (($file_types{$self->object_type}->{gen}*1 & 1)==1) {
497       my $gendata = {
498         'name'         => 'created',
499         'title'        => $main::locale->text('generated Files'),
500         'chk_action'   => 'documents_delete',
501         'chk_title'    => $main::locale->text('Delete Documents'),
502         'chkall_title' => $main::locale->text('Delete all'),
503         'file_title'   => $main::locale->text('filename'),
504         'confirm_text' => $main::locale->text('delete'),
505         'can_rename'   => 1,
506         'rename_title' => $main::locale->text('Rename Documents'),
507         'done_text'    => $main::locale->text('deleted')
508       };
509       push @sources , $gendata;
510     }
511     if (($file_types{$self->object_type}->{gen}*1 & 2)==2) {
512       my @others =  SL::File->get_other_sources();
513       $main::lxdebug->message(LXDebug->DEBUG2(), "other cnt=". scalar(@others));
514       foreach my $scanner_or_mailrx (@others) {
515         my $other = {
516           'name'         => $scanner_or_mailrx->{name},
517           'title'        => $main::locale->text('from \'#1\' imported Files',$scanner_or_mailrx->{description}),
518           'chk_action'   => $scanner_or_mailrx->{name}.'_unimport',
519           'chk_title'    => $main::locale->text('Unimport documents'),
520           'chkall_title' => $main::locale->text('Unimport all'),
521           'file_title'   => $main::locale->text('filename'),
522           'confirm_text' => $main::locale->text('unimport'),
523           'can_rename'   => 1,
524           'rename_title' => $main::locale->text('Rename Documents'),
525           'can_import'   => 1,
526           'import_title' => $main::locale->text('Add Document from \'#1\'',$scanner_or_mailrx->{name}),
527           'path'         => $scanner_or_mailrx->{directory},
528           'done_text'    => $main::locale->text('unimported')
529         };
530         push @sources , $other;
531       }
532     }
533   }
534   elsif ( $self->file_type eq 'attachment' ) {
535     my $attdata = {
536       'name'         => 'uploaded',
537       'title'        => $main::locale->text(''),
538       'chk_action'   => 'attachments_delete',
539       'chk_title'    => $main::locale->text('Delete Attachments'),
540       'chkall_title' => $main::locale->text('Delete all'),
541       'file_title'   => $main::locale->text('filename'),
542       'confirm_text' => $main::locale->text('delete'),
543       'can_rename'   => 1,
544       'are_existing' => $self->existing ? 1 : 0,
545       'rename_title' => $main::locale->text('Rename Attachments'),
546       'can_upload'   => 1,
547       'upload_title' => $main::locale->text('Upload Attachments'),
548       'done_text'    => $main::locale->text('deleted')
549     };
550     push @sources , $attdata;
551   }
552   elsif ( $self->file_type eq 'image' ) {
553     my $attdata = {
554       'name'         => 'uploaded',
555       'title'        => $main::locale->text(''),
556       'chk_action'   => 'images_delete',
557       'chk_title'    => $main::locale->text('Delete Images'),
558       'chkall_title' => $main::locale->text('Delete all'),
559       'file_title'   => $main::locale->text('filename'),
560       'confirm_text' => $main::locale->text('delete'),
561       'can_rename'   => 1,
562       'are_existing' => $self->existing ? 1 : 0,
563       'rename_title' => $main::locale->text('Rename Images'),
564       'can_upload'   => 1,
565       'upload_title' => $main::locale->text('Upload Images'),
566       'done_text'    => $main::locale->text('deleted')
567     };
568     push @sources , $attdata;
569   }
570   $main::lxdebug->message(LXDebug->DEBUG2(), "get_sources count=".scalar(@sources));
571   return @sources;
572 }
573
574 1;
575
576 __END__
577
578 =pod
579
580 =encoding utf-8
581
582 =head1 NAME
583
584 SL::Controller::File - Controller for managing files
585
586
587 =head1 SYNOPSIS
588
589 =begin text
590
591     # The Controller is called direct from the webpages
592
593
594     <a href="controller.pl?action=File/list&file_type=document\
595        &object_type=[% HTML.escape(type) %]&object_id=[% HTML.url(id) %]">
596
597
598     # or indirect via javascript functions from js/kivi.File.js
599
600
601     kivi.popup_dialog({ url:     'controller.pl',
602                         data:    { action     : 'File/ajax_upload',
603                                    file_type  : 'uploaded',
604                                    object_type: type,
605                                    object_id  : id
606                                  }
607                            ...
608
609 =end text
610
611
612 =head1 DESCRIPTION
613
614 This is a controller for handling files in a storage independent way.
615 The storage may be a Filesystem,a WebDAV, a Database or DMS.
616 These backends must be configered in ClientConfig.
617 This Controller use as intermediate layer for storage C<SL::File>.
618
619 The Controller is responsible to display forms for displaying the files at the ERP-objects and
620 for uploading and downloading the files.
621
622 More description of the intermediate layer see L<SL::File>.
623
624 =head1 METHODS
625
626 =head2 C<action_list>
627
628 This loads a list of files on a webpage. This can be done with a normal submit or via an ajax/json call.
629 Dependent of file_type different sources are available.
630
631 For documents there are the 'created' source and the imports from scanners or email.
632 For attachments and images only the 'uploaded' source available.
633
634 Available C<FORM PARAMS>:
635
636 =over 4
637
638 =item C<form.object_id>
639
640 The Id of the ERP-object.
641
642 =item C<form.object_type>
643
644 The Type of the ERP-object like "sales_quotation". A clear mapping to the class/model exists in the controller.
645
646 =item C<form.file_type>
647
648 For one ERP-object may exists different type of documents the type may be "documents","attachments" or "images".
649 This file_type is a filter for the list.
650
651 =item C<form.json>
652
653 The method can be used as normal HTTP-Request (json=0) or as AJAX-JSON call to refresh the list if the parameter is set to 1.
654
655 =back
656
657
658 =head2 C<action_ajax_upload>
659
660
661 A new file or more files can selected by a dialog and insert into the system.
662
663
664 Available C<FORM PARAMS>:
665
666 =over 4
667
668 =item C<form.file_type>
669
670 This parameter describe here the source for a new file :
671 "attachments" and "images"
672
673 This is a normal upload selection, which may be more then one file to upload.
674
675 =item C<form.object_id>
676
677 and
678
679 =item C<form.object_type>
680
681 are the same as at C<action_list>
682
683 =back
684
685 =head2  C<action_ajax_files_uploaded>
686
687 The Upload of selected Files. The "multipart_formdata" is parsed in SL::Request into the formsvariable "form.ATTACHMENTS".
688 The filepaths are checked about Unix and Windows paths. Also the MIME type of the files are verified ( IS the contents of a *.pdf real PDF?).
689 If the same filename still exists at this object after the download for each existing filename a rename dialog will be opened.
690
691 If the filename is not changed the new uploaded file is a new version of the file, if the name is changed it is a new file.
692
693 Available C<FORM PARAMS>:
694
695 =over 4
696
697 =item C<form.ATTACHMENTS.uploadfiles>
698
699 This is an array of elements which have {filename} for the name and {data} for the contents.
700
701 Also object_id, object_type and file_type
702
703 =back
704
705 =head2 C<action_download>
706
707 This is the real download of a file normally called via javascript "$.download("controller.pl", data);"
708
709 Available C<FORM PARAMS>:
710
711 =over 4
712
713 Also object_id, object_type and file_type
714
715 =back
716
717 =head2 C<action_ajax_importdialog>
718
719 A Dialog with all available and not imported files to import is open.
720 More then one file can be selected.
721
722 Available C<FORM PARAMS>:
723
724 =over 4
725
726 =item C<form.source>
727
728 The name of the source like "scanner1" or "email"
729
730 =item C<form.path>
731
732 The full path to the directory on the server, where the files to import can found
733
734 Also object_id, object_type and file_type
735
736 =back
737
738 =head2 C<action_ajax_delete>
739
740 Some files can be deleted
741
742 Available C<FORM PARAMS>:
743
744 =over 4
745
746 =item C<form.ids>
747
748 The ids of the files to delete. Only this files are deleted not all versions of a file if the exists
749
750 =back
751
752 =head2 C<action_ajax_unimport>
753
754 Some files can be unimported, dependent of the source of the file. This means they are moved
755 back to the directory of the source
756
757 Available C<FORM PARAMS>:
758
759 =over 4
760
761 =item C<form.ids>
762
763 The ids of the files to unimport. Only this files are unimported not all versions of a file if the exists
764
765 =back
766
767 =head2 C<action_ajax_rename>
768
769 One file can be renamed. There can be some checks if the same filename still exists at one object.
770
771
772 =head1 AUTHOR
773
774 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
775
776 =cut