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