Typos und spellcheck: unkown -> unknown
[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 "Unknown 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_delete'   => $::instance_conf->get_doc_delete_printfiles,
499         'can_rename'   => $::instance_conf->get_doc_delete_printfiles,
500         'rename_title' => $main::locale->text('Rename Documents'),
501         'done_text'    => $main::locale->text('deleted')
502       };
503       push @sources , $gendata;
504     }
505     if (($file_types{$self->object_type}->{gen}*1 & 2)==2) {
506       my @others =  SL::File->get_other_sources();
507       foreach my $scanner_or_mailrx (@others) {
508         my $other = {
509           'name'         => $scanner_or_mailrx->{name},
510           'title'        => $main::locale->text('from \'#1\' imported Files', $scanner_or_mailrx->{description}),
511           'chk_action'   => $scanner_or_mailrx->{name}.'_unimport',
512           'chk_title'    => $main::locale->text('Unimport documents'),
513           'chkall_title' => $main::locale->text('Unimport all'),
514           'file_title'   => $main::locale->text('filename'),
515           'confirm_text' => $main::locale->text('unimport'),
516           'can_rename'   => 1,
517           'rename_title' => $main::locale->text('Rename Documents'),
518           'can_import'   => 1,
519           'can_delete'   => 0,
520           'import_title' => $main::locale->text('Add Document from \'#1\'', $scanner_or_mailrx->{name}),
521           'path'         => $scanner_or_mailrx->{directory},
522           'done_text'    => $main::locale->text('unimported')
523         };
524         push @sources , $other;
525       }
526     }
527   }
528   elsif ( $self->file_type eq 'attachment' ) {
529     my $attdata = {
530       'name'         => 'uploaded',
531       'title'        => $main::locale->text(''),
532       'chk_action'   => 'attachments_delete',
533       'chk_title'    => $main::locale->text('Delete Attachments'),
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 Attachments'),
540       'can_upload'   => 1,
541       'can_delete'   => 1,
542       'upload_title' => $main::locale->text('Upload Attachments'),
543       'done_text'    => $main::locale->text('deleted')
544     };
545     push @sources , $attdata;
546   }
547   elsif ( $self->file_type eq 'image' ) {
548     my $attdata = {
549       'name'         => 'uploaded',
550       'title'        => $main::locale->text(''),
551       'chk_action'   => 'images_delete',
552       'chk_title'    => $main::locale->text('Delete Images'),
553       'chkall_title' => $main::locale->text('Delete all'),
554       'file_title'   => $main::locale->text('filename'),
555       'confirm_text' => $main::locale->text('delete'),
556       'can_rename'   => 1,
557       'are_existing' => $self->existing ? 1 : 0,
558       'rename_title' => $main::locale->text('Rename Images'),
559       'can_upload'   => 1,
560       'can_delete'   => 1,
561       'upload_title' => $main::locale->text('Upload Images'),
562       'done_text'    => $main::locale->text('deleted')
563     };
564     push @sources , $attdata;
565   }
566   return @sources;
567 }
568
569 1;
570
571 __END__
572
573 =pod
574
575 =encoding utf-8
576
577 =head1 NAME
578
579 SL::Controller::File - Controller for managing files
580
581 =head1 SYNOPSIS
582
583 The Controller is called directly from the webpages
584
585     <a href="controller.pl?action=File/list&file_type=document\
586        &object_type=[% HTML.escape(type) %]&object_id=[% HTML.url(id) %]">
587
588
589 or indirectly via javascript functions from js/kivi.File.js
590
591     kivi.popup_dialog({ url:     'controller.pl',
592                         data:    { action     : 'File/ajax_upload',
593                                    file_type  : 'uploaded',
594                                    object_type: type,
595                                    object_id  : id
596                                  }
597                            ...
598
599 =head1 DESCRIPTION
600
601 This is a controller for handling files in a storage independent way.
602 The storage may be a Filesystem,a WebDAV, a Database or DMS.
603 These backends must be configered in ClientConfig.
604 This Controller use as intermediate layer for storage C<SL::File>.
605
606 The Controller is responsible to display forms for displaying the files at the ERP-objects and
607 for uploading and downloading the files.
608
609 More description of the intermediate layer see L<SL::File>.
610
611 =head1 METHODS
612
613 =head2 C<action_list>
614
615 This loads a list of files on a webpage. This can be done with a normal submit or via an ajax/json call.
616 Dependent of file_type different sources are available.
617
618 For documents there are the 'created' source and the imports from scanners or email.
619 For attachments and images only the 'uploaded' source available.
620
621 Available C<FORM PARAMS>:
622
623 =over 4
624
625 =item C<form.object_id>
626
627 The Id of the ERP-object.
628
629 =item C<form.object_type>
630
631 The Type of the ERP-object like "sales_quotation". A clear mapping to the class/model exists in the controller.
632
633 =item C<form.file_type>
634
635 For one ERP-object may exists different type of documents the type may be "documents","attachments" or "images".
636 This file_type is a filter for the list.
637
638 =item C<form.json>
639
640 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.
641
642 =back
643
644
645 =head2 C<action_ajax_upload>
646
647
648 A new file or more files can selected by a dialog and insert into the system.
649
650
651 Available C<FORM PARAMS>:
652
653 =over 4
654
655 =item C<form.file_type>
656
657 This parameter describe here the source for a new file :
658 "attachments" and "images"
659
660 This is a normal upload selection, which may be more then one file to upload.
661
662 =item C<form.object_id>
663
664 and
665
666 =item C<form.object_type>
667
668 are the same as at C<action_list>
669
670 =back
671
672 =head2  C<action_ajax_files_uploaded>
673
674 The Upload of selected Files. The "multipart_formdata" is parsed in SL::Request into the formsvariable "form.ATTACHMENTS".
675 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?).
676 If the same filename still exists at this object after the download for each existing filename a rename dialog will be opened.
677
678 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.
679
680 Available C<FORM PARAMS>:
681
682 =over 4
683
684 =item C<form.ATTACHMENTS.uploadfiles>
685
686 This is an array of elements which have {filename} for the name and {data} for the contents.
687
688 Also object_id, object_type and file_type
689
690 =back
691
692 =head2 C<action_download>
693
694 This is the real download of a file normally called via javascript "$.download("controller.pl", data);"
695
696 Available C<FORM PARAMS>:
697
698 =over 4
699
700 Also object_id, object_type and file_type
701
702 =back
703
704 =head2 C<action_ajax_importdialog>
705
706 A Dialog with all available and not imported files to import is open.
707 More then one file can be selected.
708
709 Available C<FORM PARAMS>:
710
711 =over 4
712
713 =item C<form.source>
714
715 The name of the source like "scanner1" or "email"
716
717 =item C<form.path>
718
719 The full path to the directory on the server, where the files to import can found
720
721 Also object_id, object_type and file_type
722
723 =back
724
725 =head2 C<action_ajax_delete>
726
727 Some files can be deleted
728
729 Available C<FORM PARAMS>:
730
731 =over 4
732
733 =item C<form.ids>
734
735 The ids of the files to delete. Only this files are deleted not all versions of a file if the exists
736
737 =back
738
739 =head2 C<action_ajax_unimport>
740
741 Some files can be unimported, dependent of the source of the file. This means they are moved
742 back to the directory of the source
743
744 Available C<FORM PARAMS>:
745
746 =over 4
747
748 =item C<form.ids>
749
750 The ids of the files to unimport. Only these files are unimported not all versions of a file if the exists
751
752 =back
753
754 =head2 C<action_ajax_rename>
755
756 One file can be renamed. There can be some checks if the same filename still exists at one object.
757
758 =head1 AUTHOR
759
760 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
761
762 =cut