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