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