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