1 package SL::Controller::File;
 
   5 use parent qw(SL::Controller::Base);
 
   7 use List::Util qw(first max);
 
  10 use Encode qw(decode);
 
  16 use File::Spec::Win32;
 
  17 use File::MimeInfo::Magic;
 
  18 use SL::DB::Helper::Mappings;
 
  20 use SL::DB::DeliveryOrder;
 
  23 use SL::DB::PurchaseInvoice;
 
  25 use SL::DB::GLTransaction;
 
  29 use SL::Helper::CreatePDF qw(:all);
 
  30 use SL::Locale::String;
 
  33 use SL::Controller::Helper::ThumbnailCreator qw(file_probe_image_type);
 
  35 use constant DO_DELETE   => 0;
 
  36 use constant DO_UNIMPORT => 1;
 
  38 use Rose::Object::MakeMethods::Generic
 
  40     'scalar --get_set_init' => [ qw() ],
 
  41     'scalar' => [ qw(object object_type object_model object_id object_right file_type files is_global existing) ],
 
  44 __PACKAGE__->run_before('check_object_params', only => [ qw(list ajax_delete ajax_importdialog ajax_import ajax_unimport ajax_upload ajax_files_uploaded) ]);
 
  46 # gen:    bitmask: bit 1 (value is 1 or 3)    => file created
 
  47 #                  bit 2 (value is 2 or 3)    => file from other source
 
  48 # gltype: is this used somewhere?
 
  49 # dir:    is this used somewhere?
 
  50 # model:  base name of the rose model
 
  51 # right:  access right used for import
 
  53   'sales_quotation'         => { gen => 1, gltype => '',   dir =>'SalesQuotation',       model => 'Order',          right => 'import_ar'  },
 
  54   'sales_order'             => { gen => 1, gltype => '',   dir =>'SalesOrder',           model => 'Order',          right => 'import_ar'  },
 
  55   'sales_delivery_order'    => { gen => 1, gltype => '',   dir =>'SalesDeliveryOrder',   model => 'DeliveryOrder',  right => 'import_ar'  },
 
  56   'invoice'                 => { gen => 1, gltype => 'ar', dir =>'SalesInvoice',         model => 'Invoice',        right => 'import_ar'  },
 
  57   'credit_note'             => { gen => 1, gltype => '',   dir =>'CreditNote',           model => 'Invoice',        right => 'import_ar'  },
 
  58   'request_quotation'       => { gen => 3, gltype => '',   dir =>'RequestForQuotation',  model => 'Order',          right => 'import_ap'  },
 
  59   'purchase_order'          => { gen => 3, gltype => '',   dir =>'PurchaseOrder',        model => 'Order',          right => 'import_ap'  },
 
  60   'purchase_delivery_order' => { gen => 3, gltype => '',   dir =>'PurchaseDeliveryOrder',model => 'DeliveryOrder',  right => 'import_ap'  },
 
  61   'purchase_invoice'        => { gen => 2, gltype => 'ap', dir =>'PurchaseInvoice',      model => 'PurchaseInvoice',right => 'import_ap'  },
 
  62   'vendor'                  => { gen => 0, gltype => '',   dir =>'Vendor',               model => 'Vendor',         right => 'xx'         },
 
  63   'customer'                => { gen => 1, gltype => '',   dir =>'Customer',             model => 'Customer',       right => 'xx'         },
 
  64   'part'                    => { gen => 0, gltype => '',   dir =>'Part',                 model => 'Part',           right => 'xx'         },
 
  65   'gl_transaction'          => { gen => 2, gltype => 'gl', dir =>'GeneralLedger',        model => 'GLTransaction',  right => 'import_ap'  },
 
  66   'draft'                   => { gen => 0, gltype => '',   dir =>'Draft',                model => 'Draft',          right => 'xx'         },
 
  67   'csv_customer'            => { gen => 1, gltype => '',   dir =>'Reports',              model => 'Customer',       right => 'xx'         },
 
  68   'csv_vendor'              => { gen => 1, gltype => '',   dir =>'Reports',              model => 'Vendor',         right => 'xx'         },
 
  69   'shop_image'              => { gen => 0, gltype => '',   dir =>'ShopImages',           model => 'Part',           right => 'xx'         },
 
  70   'letter'                  => { gen => 3, gltype => '',   dir =>'Letter',               model => 'Letter',         right => 'sales_letter_edit | purchase_letter_edit' },
 
  74 # $main::locale->text('imported')
 
  84   $is_json = 1 if $::form->{json};
 
  86   $self->_do_list($is_json);
 
  89 sub action_ajax_importdialog {
 
  91   $::auth->assert($self->object_right);
 
  92   my $path   = $::form->{path};
 
  93   my @files  = $self->_get_from_import($path);
 
  95     'name'         => $::form->{source},
 
  97     'chk_action'   => $::form->{source}.'_import',
 
  98     'chk_title'    => $main::locale->text('Import scanned documents'),
 
  99     'chkall_title' => $main::locale->text('Import all'),
 
 102   $self->render('file/import_dialog',
 
 109 sub action_ajax_import {
 
 111   $::auth->assert($self->object_right);
 
 112   my $ids    = $::form->{ids};
 
 113   my $source = $::form->{source};
 
 114   my $path   = $::form->{path};
 
 115   my @files  = $self->_get_from_import($path);
 
 116   foreach my $filename (@{ $::form->{$ids} || [] }) {
 
 117     my ($file, undef) = grep { $_->{name} eq $filename } @files;
 
 119       my $obj = SL::File->save(object_id   => $self->object_id,
 
 120                                object_type => $self->object_type,
 
 121                                mime_type   => 'application/pdf',
 
 123                                file_type   => 'document',
 
 124                                file_name   => $file->{filename},
 
 125                                file_path   => $file->{path}
 
 127       unlink($file->{path}) if $obj;
 
 133 sub action_ajax_delete {
 
 135   $self->_delete_all(DO_DELETE, $::locale->text('Following files are deleted:'));
 
 138 sub action_ajax_unimport {
 
 140   $self->_delete_all(DO_UNIMPORT, $::locale->text('Following files are unimported:'));
 
 143 sub action_ajax_rename {
 
 145   my ($id, $version) = split /_/, $::form->{id};
 
 146   my $file = SL::File->get(id => $id);
 
 148     $self->js->flash('error', $::locale->text('File not exists !'))->render();
 
 151   my $sessionfile = $::form->{sessionfile};
 
 152   if ( $sessionfile && -f $sessionfile ) {
 
 154     if ( $::form->{to} eq $file->file_name ) {
 
 155       # no rename so use as new version
 
 156       $file->save_file($sessionfile);
 
 157       $self->js->flash('warning', $::locale->text('File \'#1\' is used as new Version !', $file->file_name));
 
 160       # new filename, so it is a new file with the same attributes as the old file
 
 162         SL::File->save(object_id   => $file->object_id,
 
 163                        object_type => $file->object_type,
 
 164                        mime_type   => $file->mime_type,
 
 165                        source      => $file->source,
 
 166                        file_type   => $file->file_type,
 
 167                        file_name   => $::form->{to},
 
 168                        file_path   => $sessionfile
 
 170         unlink($sessionfile);
 
 173         $self->js->flash(       'error', t8('internal error (see details)'))
 
 174                  ->flash_detail('error', $@)->render;
 
 184       $result = $file->rename($::form->{to});
 
 187       $self->js->flash(       'error', t8('internal error (see details)'))
 
 188                ->flash_detail('error', $@)->render;
 
 192     if ($result != SL::File::RENAME_OK) {
 
 193       $self->js->flash('error',
 
 194                          $result == SL::File::RENAME_EXISTS ? $::locale->text('File still exists !')
 
 195                        : $result == SL::File::RENAME_SAME   ? $::locale->text('Same Filename !')
 
 196                        :                                      $::locale->text('File not exists !'))
 
 201   $self->is_global($::form->{is_global});
 
 202   $self->file_type(  $file->file_type);
 
 203   $self->object_type($file->object_type);
 
 204   $self->object_id(  $file->object_id);
 
 205   #$self->object_model($file_types{$file->module}->{model});
 
 206   #$self->object_right($file_types{$file->module}->{right});
 
 207   if ( $::form->{next_ids} ) {
 
 208     my @existing = split(/,/, $::form->{next_ids});
 
 209     $self->existing(\@existing);
 
 214 sub action_ajax_upload {
 
 216   $self->{maxsize} = $::instance_conf->get_doc_max_filesize;
 
 217   $self->{accept_types} = '';
 
 218   $self->{accept_types} = 'image/png,image/gif,image/jpeg,image/tiff,*png,*gif,*.jpg,*.tif' if $self->{file_type} eq 'image';
 
 219   $self->render('file/upload_dialog',
 
 225 sub action_ajax_files_uploaded {
 
 228   my $source = 'uploaded';
 
 230   if ( $::form->{ATTACHMENTS}->{uploadfiles} ) {
 
 231     my @upfiles = @{ $::form->{ATTACHMENTS}->{uploadfiles} };
 
 232     foreach my $idx (0 .. scalar(@upfiles) - 1) {
 
 234         my $fname = uri_unescape($upfiles[$idx]->{filename});
 
 235         # normalize and find basename
 
 236         # first split with unix rules
 
 237         # after that split with windows rules
 
 238         my ($volume, $directories, $basefile) = File::Spec::Unix->splitpath($fname);
 
 239         ($volume, $directories, $basefile) = File::Spec::Win32->splitpath($basefile);
 
 241         # to find real mime_type by magic we must save the filedata
 
 243         my $sess_fname = "file_upload_" . $self->object_type . "_" . $self->object_id . "_" . $idx;
 
 244         my $sfile      = SL::SessionFile->new($sess_fname, mode => 'w');
 
 246         $sfile->fh->print(${$upfiles[$idx]->{data}});
 
 248         my $mime_type = File::MimeInfo::Magic::magic($sfile->file_name);
 
 251           # if filename has the suffix "pdf", but isn't really a pdf, set mimetype for no suffix
 
 252           $mime_type = File::MimeInfo::Magic::mimetype($basefile);
 
 253           $mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
 
 255         if ( $self->file_type eq 'image' && $self->file_probe_image_type($mime_type, $basefile)) {
 
 258         my ($existobj) = SL::File->get_all(object_id   => $self->object_id,
 
 259                                            object_type => $self->object_type,
 
 260                                            mime_type   => $mime_type,
 
 262                                            file_type   => $self->file_type,
 
 263                                            file_name   => $basefile,
 
 267           push @existing, $existobj->id.'_'.$sfile->file_name;
 
 269           my $fileobj = SL::File->save(object_id        => $self->object_id,
 
 270                                        object_type      => $self->object_type,
 
 271                                        mime_type        => $mime_type,
 
 273                                        file_type        => $self->file_type,
 
 274                                        file_name        => $basefile,
 
 275                                        title            => $::form->{title},
 
 276                                        description      => $::form->{description},
 
 277                                        ## two possibilities: what is better ? content or sessionfile ??
 
 278                                        file_contents    => ${$upfiles[$idx]->{data}},
 
 279                                        file_path        => $sfile->file_name
 
 281           unlink($sfile->file_name);
 
 285         $self->js->flash(       'error', t8('internal error (see details)'))
 
 286                  ->flash_detail('error', $@)->render;
 
 291   $self->existing(\@existing);
 
 295 sub action_download {
 
 297   my ($id, $version) = split /_/, $::form->{id};
 
 298   my $file = SL::File->get(id => $id );
 
 299   $file->version($version) if $version;
 
 300   my $ref  = $file->get_content;
 
 301   if ( $file && $ref ) {
 
 302     return $self->send_file($ref,
 
 303       type => $file->mime_type,
 
 304       name => $file->file_name,
 
 313 sub check_object_params {
 
 316   my $id      = ($::form->{object_id} // 0) * 1;
 
 317   my $draftid = ($::form->{draft_id}  // 0) * 1;
 
 321   if ( $draftid == 0 && $id == 0 && $::form->{is_global} ) {
 
 323     $type  = $::form->{object_type};
 
 326     $id   = $::form->{draft_id};
 
 328   } elsif ( $::form->{object_type} ) {
 
 329     $type = $::form->{object_type};
 
 331   die "No object type"      unless $type;
 
 332   die "No file type"        unless $::form->{file_type};
 
 333   die "Unknown object type" unless $file_types{$type};
 
 335   $self->is_global($gldoc);
 
 336   $self->file_type($::form->{file_type});
 
 337   $self->object_type($type);
 
 338   $self->object_id($id);
 
 339   $self->object_model($file_types{$type}->{model});
 
 340   $self->object_right($file_types{$type}->{right});
 
 342  # $::auth->assert($self->object_right);
 
 344  # my $model = 'SL::DB::' . $self->object_model;
 
 345  # $self->object($model->new(id => $self->object_id)->load || die "Record not found");
 
 355   my ($self, $do_unimport, $infotext) = @_;
 
 357   my $ids = $::form->{ids};
 
 358   foreach my $id_version (@{ $::form->{$ids} || [] }) {
 
 359     my ($id, $version) = split /_/, $id_version;
 
 360     my $dbfile = SL::File->get(id => $id);
 
 363         $dbfile->version($version);
 
 364         $files .= ' ' . $dbfile->file_name if $dbfile->delete_version;
 
 366         $files .= ' ' . $dbfile->file_name if $dbfile->delete;
 
 370   $self->js->flash('info', $infotext . $files) if $files;
 
 375   my ($self, $json) = @_;
 
 377   if ( $self->file_type eq 'document' ) {
 
 379     push @object_types, $self->object_type;
 
 380     push @object_types, qw(dunning dunning1 dunning2 dunning3) if $self->object_type eq 'invoice'; # hardcoded object types?
 
 381     @files = SL::File->get_all_versions(object_id   => $self->object_id,
 
 382                                         object_type => \@object_types,
 
 383                                         file_type   => $self->file_type,
 
 387   elsif ( $self->file_type eq 'attachment' || $self->file_type eq 'image' ) {
 
 388     @files   = SL::File->get_all(object_id   => $self->object_id,
 
 389                                  object_type => $self->object_type,
 
 390                                  file_type   => $self->file_type,
 
 393   $self->files(\@files);
 
 395   if($self->object_type eq 'shop_image'){
 
 397       ->run('kivi.ShopPart.show_images', $self->object_id)
 
 400     $self->_mk_render('file/list', 1, 0, $json);
 
 404 sub _get_from_import {
 
 405   my ($self, $path) = @_;
 
 408   my $language = $::lx_office_conf{system}->{language};
 
 409   my $timezone = $::locale->get_local_time_zone()->name;
 
 410   if (opendir my $dir, $path) {
 
 411     my @files = (readdir $dir);
 
 412     foreach my $file ( @files) {
 
 413       next if (($file eq '.') || ($file eq '..'));
 
 414       $file = Encode::decode('utf-8', $file);
 
 416       next if ( -d "$path/$file" );
 
 418       my $tmppath = File::Spec->catfile( $path, $file );
 
 419       next if( ! -f $tmppath );
 
 421       my $st = stat($tmppath);
 
 422       my $dt = DateTime->from_epoch( epoch => $st->mtime, time_zone => $timezone, locale => $language );
 
 423       my $sname = $main::locale->quote_special_chars('HTML', $file);
 
 426         'filename' => $sname,
 
 428         'mtime'    => $st->mtime,
 
 429         'date'     => $dt->dmy('.') . " " . $dt->hms,
 
 436     $::lxdebug->message(LXDebug::WARN(), "SL::File::_get_from_import opendir failed to open dir " . $path);
 
 443   my ($self, $template, $edit, $scanner, $json) = @_;
 
 446     ##TODO make code configurable
 
 449     my @sources = $self->_get_sources();
 
 450     foreach my $source ( @sources ) {
 
 451       @{$source->{files}} = grep { $_->source eq $source->{name}} @{ $self->files };
 
 453     if ( $self->file_type eq 'document' ) {
 
 454       $title = $main::locale->text('Documents');
 
 455     } elsif ( $self->file_type eq 'attachment' ) {
 
 456       $title = $main::locale->text('Attachments');
 
 457     } elsif ( $self->file_type eq 'image' ) {
 
 458       $title = $main::locale->text('Images');
 
 461     my $output         = SL::Presenter->get->render(
 
 464       SOURCES          => \@sources,
 
 465       edit_attachments => $edit,
 
 466       object_type      => $self->object_type,
 
 467       object_id        => $self->object_id,
 
 468       file_type        => $self->file_type,
 
 469       is_global        => $self->is_global,
 
 473       $self->js->html('#'.$self->file_type.'_list_'.$self->object_type, $output);
 
 474       if ( $self->existing && scalar(@{$self->existing}) > 0) {
 
 475         my $first = shift @{$self->existing};
 
 476         my ($first_id, $sfile) = split('_', $first, 2);
 
 477         my $file = SL::File->get(id => $first_id );
 
 478         $self->js->run('kivi.File.askForRename', $first_id, $file->file_type, $file->file_name, $sfile, join (',', @{$self->existing}), $self->is_global);
 
 482         $self->render(\$output, { layout => 0, process => 0 });
 
 487       $self->js->flash(       'error', t8('internal error (see details)'))
 
 488                ->flash_detail('error', $@)->render;
 
 490       $self->render('generic/error', { layout => 0 }, label_error => $@);
 
 499   if ( $self->file_type eq 'document' ) {
 
 500     # TODO statt gen neue attribute in filetypes :
 
 501     if (($file_types{$self->object_type}->{gen}*1 & 1)==1) {
 
 504         'title'        => $main::locale->text('generated Files'),
 
 505         'chk_action'   => 'documents_delete',
 
 506         'chk_title'    => $main::locale->text('Delete Documents'),
 
 507         'chkall_title' => $main::locale->text('Delete all'),
 
 508         'file_title'   => $main::locale->text('filename'),
 
 509         'confirm_text' => $main::locale->text('delete'),
 
 510         'can_delete'   => $::instance_conf->get_doc_delete_printfiles,
 
 511         'can_rename'   => $::instance_conf->get_doc_delete_printfiles,
 
 512         'rename_title' => $main::locale->text('Rename Documents'),
 
 513         'done_text'    => $main::locale->text('deleted')
 
 515       push @sources , $gendata;
 
 517     if (($file_types{$self->object_type}->{gen}*1 & 2)==2) {
 
 518       my @others =  SL::File->get_other_sources();
 
 519       foreach my $scanner_or_mailrx (@others) {
 
 521           'name'         => $scanner_or_mailrx->{name},
 
 522           'title'        => $main::locale->text('from \'#1\' imported Files', $scanner_or_mailrx->{description}),
 
 523           'chk_action'   => $scanner_or_mailrx->{name}.'_unimport',
 
 524           'chk_title'    => $main::locale->text('Unimport documents'),
 
 525           'chkall_title' => $main::locale->text('Unimport all'),
 
 526           'file_title'   => $main::locale->text('filename'),
 
 527           'confirm_text' => $main::locale->text('unimport'),
 
 529           'rename_title' => $main::locale->text('Rename Documents'),
 
 532           'import_title' => $main::locale->text('Add Document from \'#1\'', $scanner_or_mailrx->{name}),
 
 533           'path'         => $scanner_or_mailrx->{directory},
 
 534           'done_text'    => $main::locale->text('unimported')
 
 536         push @sources , $other;
 
 540   elsif ( $self->file_type eq 'attachment' ) {
 
 542       'name'         => 'uploaded',
 
 543       'title'        => $main::locale->text(''),
 
 544       'chk_action'   => 'attachments_delete',
 
 545       'chk_title'    => $main::locale->text('Delete Attachments'),
 
 546       'chkall_title' => $main::locale->text('Delete all'),
 
 547       'file_title'   => $main::locale->text('filename'),
 
 548       'confirm_text' => $main::locale->text('delete'),
 
 550       'are_existing' => $self->existing ? 1 : 0,
 
 551       'rename_title' => $main::locale->text('Rename Attachments'),
 
 554       'upload_title' => $main::locale->text('Upload Attachments'),
 
 555       'done_text'    => $main::locale->text('deleted')
 
 557     push @sources , $attdata;
 
 559   elsif ( $self->file_type eq 'image' ) {
 
 561       'name'         => 'uploaded',
 
 562       'title'        => $main::locale->text(''),
 
 563       'chk_action'   => 'images_delete',
 
 564       'chk_title'    => $main::locale->text('Delete Images'),
 
 565       'chkall_title' => $main::locale->text('Delete all'),
 
 566       'file_title'   => $main::locale->text('filename'),
 
 567       'confirm_text' => $main::locale->text('delete'),
 
 569       'are_existing' => $self->existing ? 1 : 0,
 
 570       'rename_title' => $main::locale->text('Rename Images'),
 
 573       'upload_title' => $main::locale->text('Upload Images'),
 
 574       'done_text'    => $main::locale->text('deleted')
 
 576     push @sources , $attdata;
 
 591 SL::Controller::File - Controller for managing files
 
 595 The Controller is called directly from the webpages
 
 597     <a href="controller.pl?action=File/list&file_type=document\
 
 598        &object_type=[% HTML.escape(type) %]&object_id=[% HTML.url(id) %]">
 
 601 or indirectly via javascript functions from js/kivi.File.js
 
 603     kivi.popup_dialog({ url:     'controller.pl',
 
 604                         data:    { action     : 'File/ajax_upload',
 
 605                                    file_type  : 'uploaded',
 
 613 This is a controller for handling files in a storage independent way.
 
 614 The storage may be a Filesystem,a WebDAV, a Database or DMS.
 
 615 These backends must be configered in ClientConfig.
 
 616 This Controller use as intermediate layer for storage C<SL::File>.
 
 618 The Controller is responsible to display forms for displaying the files at the ERP-objects and
 
 619 for uploading and downloading the files.
 
 621 More description of the intermediate layer see L<SL::File>.
 
 625 =head2 C<action_list>
 
 627 This loads a list of files on a webpage. This can be done with a normal submit or via an ajax/json call.
 
 628 Dependent of file_type different sources are available.
 
 630 For documents there are the 'created' source and the imports from scanners or email.
 
 631 For attachments and images only the 'uploaded' source available.
 
 633 Available C<FORM PARAMS>:
 
 637 =item C<form.object_id>
 
 639 The Id of the ERP-object.
 
 641 =item C<form.object_type>
 
 643 The Type of the ERP-object like "sales_quotation". A clear mapping to the class/model exists in the controller.
 
 645 =item C<form.file_type>
 
 647 For one ERP-object may exists different type of documents the type may be "documents","attachments" or "images".
 
 648 This file_type is a filter for the list.
 
 652 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.
 
 657 =head2 C<action_ajax_upload>
 
 660 A new file or more files can selected by a dialog and insert into the system.
 
 663 Available C<FORM PARAMS>:
 
 667 =item C<form.file_type>
 
 669 This parameter describe here the source for a new file :
 
 670 "attachments" and "images"
 
 672 This is a normal upload selection, which may be more then one file to upload.
 
 674 =item C<form.object_id>
 
 678 =item C<form.object_type>
 
 680 are the same as at C<action_list>
 
 684 =head2  C<action_ajax_files_uploaded>
 
 686 The Upload of selected Files. The "multipart_formdata" is parsed in SL::Request into the formsvariable "form.ATTACHMENTS".
 
 687 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?).
 
 688 If the same filename still exists at this object after the download for each existing filename a rename dialog will be opened.
 
 690 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.
 
 692 Available C<FORM PARAMS>:
 
 696 =item C<form.ATTACHMENTS.uploadfiles>
 
 698 This is an array of elements which have {filename} for the name and {data} for the contents.
 
 700 Also object_id, object_type and file_type
 
 704 =head2 C<action_download>
 
 706 This is the real download of a file normally called via javascript "$.download("controller.pl", data);"
 
 708 Available C<FORM PARAMS>:
 
 712 Also object_id, object_type and file_type
 
 716 =head2 C<action_ajax_importdialog>
 
 718 A Dialog with all available and not imported files to import is open.
 
 719 More then one file can be selected.
 
 721 Available C<FORM PARAMS>:
 
 727 The name of the source like "scanner1" or "email"
 
 731 The full path to the directory on the server, where the files to import can found
 
 733 Also object_id, object_type and file_type
 
 737 =head2 C<action_ajax_delete>
 
 739 Some files can be deleted
 
 741 Available C<FORM PARAMS>:
 
 747 The ids of the files to delete. Only this files are deleted not all versions of a file if the exists
 
 751 =head2 C<action_ajax_unimport>
 
 753 Some files can be unimported, dependent of the source of the file. This means they are moved
 
 754 back to the directory of the source
 
 756 Available C<FORM PARAMS>:
 
 762 The ids of the files to unimport. Only these files are unimported not all versions of a file if the exists
 
 766 =head2 C<action_ajax_rename>
 
 768 One file can be renamed. There can be some checks if the same filename still exists at one object.
 
 772 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>