--- /dev/null
+package SL::Controller::File;
+
+use strict;
+
+use parent qw(SL::Controller::Base);
+
+use List::Util qw(first max);
+
+use utf8;
+use Encode qw(decode);
+use URI::Escape;
+use Cwd;
+use DateTime;
+use File::stat;
+use File::Spec::Unix;
+use File::Spec::Win32;
+use File::MimeInfo::Magic;
+use SL::DB::Helper::Mappings;
+use SL::DB::Order;
+use SL::DB::DeliveryOrder;
+use SL::DB::Invoice;
+
+use SL::DB::PurchaseInvoice;
+use SL::DB::Part;
+use SL::DB::GLTransaction;
+use SL::DB::Draft;
+use SL::DB::History;
+use SL::JSON;
+use SL::Helper::CreatePDF qw(:all);
+use SL::Locale::String;
+use SL::SessionFile;
+use SL::File;
+use SL::Controller::Helper::ThumbnailCreator qw(file_probe_image_type);
+
+use constant DO_DELETE => 0;
+use constant DO_UNIMPORT => 1;
+
+
+use Rose::Object::MakeMethods::Generic
+(
+ 'scalar --get_set_init' => [ qw() ],
+ 'scalar' => [ qw(object object_type object_model object_id object_right file_type files is_global existing) ],
+);
+
+__PACKAGE__->run_before('check_object_params', only => [ qw(list ajax_delete ajax_importdialog ajax_import ajax_unimport ajax_upload ajax_files_uploaded) ]);
+
+my %file_types = (
+ 'sales_quotation' => { gen => 1 ,gltype => '', dir =>'SalesQuotation', model => 'Order', right => 'import_ar' },
+ 'sales_order' => { gen => 1 ,gltype => '', dir =>'SalesOrder', model => 'Order', right => 'import_ar' },
+ 'sales_delivery_order' => { gen => 1 ,gltype => '', dir =>'SalesDeliveryOrder', model => 'DeliveryOrder', right => 'import_ar' },
+ 'invoice' => { gen => 1 ,gltype => 'ar', dir =>'SalesInvoice', model => 'Invoice', right => 'import_ar' },
+ 'credit_note' => { gen => 1 ,gltype => '', dir =>'CreditNote', model => 'Invoice', right => 'import_ar' },
+ 'request_quotation' => { gen => 3 ,gltype => '', dir =>'RequestForQuotation', model => 'Order', right => 'import_ap' },
+ 'purchase_order' => { gen => 3 ,gltype => '', dir =>'PurchaseOrder', model => 'Order', right => 'import_ap' },
+ 'purchase_delivery_order' => { gen => 3 ,gltype => '', dir =>'PurchaseDeliveryOrder',model => 'DeliveryOrder', right => 'import_ap' },
+ 'purchase_invoice' => { gen => 2 ,gltype => 'ap', dir =>'PurchaseInvoice', model => 'PurchaseInvoice',right => 'import_ap' },
+ 'vendor' => { gen => 0 ,gltype => '', dir =>'Vendor', model => 'Vendor', right => 'xx' },
+ 'customer' => { gen => 1 ,gltype => '', dir =>'Customer', model => 'Customer', right => 'xx' },
+ 'part' => { gen => 0 ,gltype => '', dir =>'Part', model => 'Part', right => 'xx' },
+ 'gl_transaction' => { gen => 2 ,gltype => 'gl', dir =>'GeneralLedger', model => 'GLTransaction', right => 'import_ap' },
+ 'draft' => { gen => 0 ,gltype => '', dir =>'Draft', model => 'Draft', right => 'xx' },
+ 'csv_customer' => { gen => 1 ,gltype => '', dir =>'Reports', model => 'Customer', right => 'xx' },
+ 'csv_vendor' => { gen => 1 ,gltype => '', dir =>'Reports', model => 'Vendor', right => 'xx' },
+);
+
+#--- 4 locale ---#
+# $main::locale->text('imported')
+
+#
+# actions
+#
+
+sub action_list {
+ my ($self) = @_;
+
+ my $isjson = 0;
+ $isjson = 1 if $::form->{json};
+
+ $self->_do_list($isjson);
+}
+
+sub action_ajax_importdialog {
+ my ($self) = @_;
+ $::auth->assert($self->object_right);
+ my $path = $::form->{path};
+ my @files = $self->_get_from_import($path);
+ my $source = {
+ 'name' => $::form->{source},
+ 'path' => $path ,
+ 'chk_action' => $::form->{source}.'_import',
+ 'chk_title' => $main::locale->text('Import scanned documents'),
+ 'chkall_title' => $main::locale->text('Import all'),
+ 'files' => \@files
+ };
+ $self->render('file/import_dialog',
+ { layout => 0
+ },
+ source => $source
+ );
+}
+
+sub action_ajax_import {
+ my ($self) = @_;
+ $::auth->assert($self->object_right);
+ my $ids = $::form->{ids};
+ my $source = $::form->{source};
+ my $path = $::form->{path};
+ my @files = $self->_get_from_import($path);
+ foreach my $filename (@{ $::form->{$ids} || [] }) {
+ my ($file,undef) = grep { $_->{name} eq $filename } @files;
+ if ( $file ) {
+ my $obj = SL::File->save(object_id => $self->object_id,
+ object_type => $self->object_type,
+ mime_type => 'application/pdf',
+ source => $source,
+ file_type => 'document',
+ file_name => $file->{filename},
+ file_path => $file->{path}
+ );
+ unlink($file->{path}) if $obj;
+ }
+ }
+ $self->_do_list(1);
+}
+
+sub action_ajax_delete {
+ my ($self) = @_;
+ $self->_delete_all(DO_DELETE,$::locale->text('Following files are deleted:'));
+}
+
+sub action_ajax_unimport {
+ my ($self) = @_;
+ $self->_delete_all(DO_UNIMPORT,$::locale->text('Following files are unimported:'));
+}
+
+sub action_ajax_rename {
+ my ($self) = @_;
+ my $file = SL::File->get(id => $::form->{id});
+ if ( ! $file ) {
+ $self->js->flash('error',$::locale->text('File not exists !'))->render();
+ return;
+ }
+ $main::lxdebug->message(LXDebug->DEBUG2(), "object_id=".$file->object_id." object_type=".$file->object_type." dbfile=".$file);
+ my $sessionfile = $::form->{sessionfile};
+ $main::lxdebug->message(LXDebug->DEBUG2(), "sessionfile=".$sessionfile);
+ if ( $sessionfile && -f $sessionfile ) {
+ $main::lxdebug->message(LXDebug->DEBUG2(), "file=".$file->file_name." to=".$::form->{to}." sessionfile=".$sessionfile);
+ # new uploaded file
+ if ( $::form->{to} eq $file->file_name ) {
+ # no rename so use as new version
+ $file->save_file($sessionfile);
+ $self->js->flash('warning',$::locale->text('File \'#1\' is used as new Version !',$file->file_name));
+
+ } else {
+ # new filename so it is a new file with same attributes as old file
+ eval {
+ SL::File->save(object_id => $file->object_id,
+ object_type => $file->object_type,
+ mime_type => $file->mime_type,
+ source => $file->source,
+ file_type => $file->file_type,
+ file_name => $::form->{to},
+ file_path => $sessionfile
+ );
+ unlink($sessionfile);
+ 1;
+ } or do {
+ $self->js->flash( 'error', t8('internal error (see details)'))
+ ->flash_detail('error', $@)->render;
+ return;
+ }
+ }
+
+ } else {
+ # normal rename
+ eval {
+ my $res = $file->rename($::form->{to});
+ $main::lxdebug->message(LXDebug->DEBUG2(), "rename result=".$res);
+ if ($res > SL::File::RENAME_OK) {
+ $self->js->flash('error',
+ $res == SL::File::RENAME_EXISTS ? $::locale->text('File still exists !')
+ : $res == SL::File::RENAME_SAME ? $::locale->text('Same Filename !')
+ : $::locale->text('File not exists !'))->render;
+ return 1;
+ }
+ 1;
+ } or do {
+ $self->js->flash( 'error', t8('internal error (see details)'))
+ ->flash_detail('error', $@)->render;
+ return;
+ }
+ }
+ $self->is_global($::form->{is_global});
+ $self->file_type( $file->file_type);
+ $self->object_type($file->object_type);
+ $self->object_id( $file->object_id);
+ #$self->object_model($file_types{$file->module}->{model});
+ #$self->object_right($file_types{$file->module}->{right});
+ if ( $::form->{next_ids} ) {
+ my @existing = split(/,/, $::form->{next_ids});
+ $self->existing(\@existing);
+ }
+ $self->_do_list(1);
+}
+
+sub action_ajax_upload {
+ my ($self) = @_;
+ $self->{maxsize} = $::instance_conf->get_doc_max_filesize;
+ $self->{accept_types} = '';
+ $self->{accept_types} = 'image/png,image/gif,image/jpeg,image/tiff,*png,*gif,*.jpg,*.tif' if $self->{file_type} eq 'image';
+ $self->render('file/upload_dialog',
+ { layout => 0
+ },
+ );
+}
+
+sub action_ajax_files_uploaded {
+ my ($self) = @_;
+
+ my $source = 'uploaded';
+ $main::lxdebug->message(LXDebug->DEBUG2(), "file_upload UPLOAD=".$::form->{ATTACHMENTS}->{uploadfiles});
+ my @existing;
+ if ( $::form->{ATTACHMENTS}->{uploadfiles} ) {
+ my @upfiles = @{ $::form->{ATTACHMENTS}->{uploadfiles} };
+ foreach my $idx (0 .. scalar(@upfiles) - 1) {
+ eval {
+ my $fname = uri_unescape($upfiles[$idx]->{filename});
+ $main::lxdebug->message(LXDebug->DEBUG2(), "file_upload name=".$fname);
+ ## normalize and find basename
+ # first split with unix rules
+ # after that split with windows rules
+ my ($volume,$directories,$basefile) = File::Spec::Unix->splitpath($fname);
+ ($volume,$directories,$basefile) = File::Spec::Win32->splitpath($basefile);
+
+ # to find real mime_type by magic we must save the filedata
+
+ my $sess_fname = "file_upload_".$self->object_type."_".$self->object_id."_".$idx;
+ my $sfile = SL::SessionFile->new($sess_fname, mode => 'w');
+
+ $sfile->fh->print(${$upfiles[$idx]->{data}});
+ $sfile->fh->close;
+ my $mime_type = File::MimeInfo::Magic::magic($sfile->file_name);
+
+ if (! $mime_type) {
+ # if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix
+ $mime_type = File::MimeInfo::Magic::mimetype($basefile);
+ $mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
+ }
+ $main::lxdebug->message(LXDebug->DEBUG2(), "mime_type=".$mime_type);
+ if ( $self->file_type eq 'image' && $self->file_probe_image_type($mime_type, $basefile)) {
+ next;
+ }
+ my ($existobj) = SL::File->get_all(object_id => $self->object_id,
+ object_type => $self->object_type,
+ mime_type => $mime_type,
+ source => $source,
+ file_type => $self->file_type,
+ file_name => $basefile,
+ );
+
+ $main::lxdebug->message(LXDebug->DEBUG2(), "store1 exist=".$existobj);
+ if ($existobj) {
+ $main::lxdebug->message(LXDebug->DEBUG2(), "id=".$existobj->id." sessionfile=". $sfile->file_name);
+ push @existing, $existobj->id.'_'.$sfile->file_name;
+ } else {
+ my $fileobj = SL::File->save(object_id => $self->object_id,
+ object_type => $self->object_type,
+ mime_type => $mime_type,
+ source => $source,
+ file_type => $self->file_type,
+ file_name => $basefile,
+ ## two possibilities: what is better ? content or sessionfile ??
+ #file_contents => ${$upfiles[$idx]->{data}},
+ file_path => $sfile->file_name
+ );
+ $main::lxdebug->message(LXDebug->DEBUG2(), "obj=".$fileobj);
+ unlink($sfile->file_name);
+ }
+ 1;
+ } or do {
+ $self->js->flash( 'error', t8('internal error (see details)'))
+ ->flash_detail('error', $@)->render;
+ return;
+ }
+ }
+ }
+ $self->existing(\@existing);
+ $self->_do_list(1);
+}
+
+sub action_download {
+ my ($self) = @_;
+ my ($id,$version) = split /_/, $::form->{id};
+ my $file = SL::File->get(id => $id );
+ $file->version($version) if $version;
+ my $ref = $file->get_content;
+ if ( $file && $ref ) {
+ return $self->send_file($ref,
+ type => $file->mime_type,
+ name => $file->file_name,
+ );
+ }
+}
+
+#
+# filters
+#
+
+sub check_object_params {
+ my ($self) = @_;
+
+ my $id = $::form->{object_id} +0;
+ my $draftid = $::form->{draft_id} +0;
+ my $gldoc = 0;
+ my $type = undef;
+
+ if ( $draftid == 0 && $id == 0 && $::form->{is_global} ) {
+ $gldoc = 1;
+ $type = $::form->{object_type};
+ }
+ elsif ( $id == 0 ) {
+ $id = $::form->{draft_id};
+ $type = 'draft';
+ } elsif ( $::form->{object_type} ) {
+ $type = $::form->{object_type};
+ }
+ die "No object type" if ! $type;
+ die "No file type" if ! $::form->{file_type};
+ die "Unkown object type" if ! $file_types{$type};
+
+ $self->is_global($gldoc);
+ $self->file_type($::form->{file_type});
+ $self->object_type($type);
+ $self->object_id($id);
+ $self->object_model($file_types{$type}->{model});
+ $self->object_right($file_types{$type}->{right});
+ $main::lxdebug->message(LXDebug->DEBUG2(), "checked: object_id=".$self->object_id." object_type=".$self->object_type." is_global=".$self->is_global);
+
+ # $::auth->assert($self->object_right);
+
+ # my $model = 'SL::DB::' . $self->object_model;
+ # $self->object($model->new(id => $self->object_id)->load || die "Record not found");
+
+ return 1;
+}
+
+#
+# private methods
+#
+
+sub _delete_all {
+ my ($self,$do_unimport,$infotext) = @_;
+ my $files = '';
+ my $ids = $::form->{ids};
+ foreach my $id_version (@{ $::form->{$ids} || [] }) {
+ my ($id,$version) = split /_/, $id_version;
+ my $dbfile = SL::File->get(id => $id);
+ $dbfile->version($version) if $dbfile && $version;
+ if ( $dbfile && $dbfile->delete ) {
+ $files .= ' '.$dbfile->file_name;
+ }
+ }
+ $self->js->flash('info',$infotext.$files) if $files;
+ $self->_do_list(1);
+}
+
+sub _do_list {
+ my ($self,$json) = @_;
+ my @files;
+ $main::lxdebug->message(LXDebug->DEBUG2(), "do_list: object_id=".$self->object_id." object_type=".$self->object_type." file_type=".$self->file_type." json=".$json);
+ if ( $self->file_type eq 'document' ) {
+ @files = SL::File->get_all_versions(object_id => $self->object_id ,
+ object_type => $self->object_type,
+ file_type => $self->file_type );
+
+ $main::lxdebug->message(LXDebug->DEBUG2(), "cnt1=".scalar(@files));
+ }
+ elsif ( $self->file_type eq 'attachment' || $self->file_type eq 'image' ) {
+ @files = SL::File->get_all(object_id => $self->object_id ,
+ object_type => $self->object_type,
+ file_type => $self->file_type );
+ $main::lxdebug->message(LXDebug->DEBUG2(), "cnt2=".scalar(@files));
+ }
+ $self->files(\@files);
+ $self->_mk_render('file/list',1,0,$json);
+}
+
+sub _get_from_import {
+ my ($self,$path) = @_;
+ my @foundfiles ;
+
+ $main::lxdebug->message(LXDebug->DEBUG2(), "import path=".$path);
+ my $language = $::lx_office_conf{system}->{language};
+ my $timezone = $::locale->get_local_time_zone()->name;
+ if (opendir my $dir, $path) {
+ my @files = ( readdir $dir);
+ foreach my $file ( @files) {
+ next if (($file eq '.') || ($file eq '..'));
+ $file = Encode::decode('utf-8', $file);
+ $main::lxdebug->message(LXDebug->DEBUG2(), "file=".$file);
+
+ next if( -d "$path/$file");
+
+ my $tmppath = File::Spec->catfile( $path, $file );
+ $main::lxdebug->message(LXDebug->DEBUG2(), "tmppath=".$tmppath." file=".$file);
+ next if( ! -f $tmppath);
+
+ my $st = stat($tmppath);
+ my $dt = DateTime->from_epoch( epoch => $st->mtime, time_zone => $timezone, locale => $language);
+ my $sname = $main::locale->quote_special_chars('HTML',$file);
+ push @foundfiles , {
+ 'name' => $file,
+ 'filename' => $sname,
+ 'path' => $tmppath,
+ 'mtime' => $st->mtime,
+ 'date' => $dt->dmy('.')." ".$dt->hms,
+ };
+
+ }
+ }
+ $main::lxdebug->message(LXDebug->DEBUG2(), "return ".scalar(@foundfiles)." files");
+ return @foundfiles;
+}
+
+sub _mk_render {
+ my ($self,$template,$edit,$scanner,$json) = @_;
+ my $err;
+ eval {
+ ##TODO here a configurable code must be implemented
+
+ my $title;
+ $main::lxdebug->message(LXDebug->DEBUG2(), "mk_render: object_id=".$self->object_id." object_type=".$self->object_type.
+ " file_type=".$self->file_type." json=".$json." filecount=".scalar(@{ $self->files })." is_global=".$self->is_global);
+ my @sources = $self->_get_sources();
+ foreach my $source ( @sources ) {
+ $main::lxdebug->message(LXDebug->DEBUG2(), "mk_render: source name=".$source->{name});
+ @{$source->{files}} = grep { $_->source eq $source->{name}} @{ $self->files };
+ }
+ if ( $self->file_type eq 'document' ) {
+ $title = $main::locale->text('Documents');
+ } elsif ( $self->file_type eq 'attachment' ) {
+ $title = $main::locale->text('Attachments');
+ } elsif ( $self->file_type eq 'image' ) {
+ $title = $main::locale->text('Images');
+ }
+
+ my $output = SL::Presenter->get->render(
+ $template,
+ title => $title,
+ SOURCES => \@sources,
+ edit_attachments => $edit,
+ object_type => $self->object_type,
+ object_id => $self->object_id,
+ file_type => $self->file_type,
+ is_global => $self->is_global,
+ json => $json,
+ );
+ if ( $json ) {
+ $self->js->html('#'.$self->file_type.'_list_'.$self->object_type, $output);
+ if ( $self->existing && scalar(@{$self->existing}) > 0) {
+ my $first = shift @{$self->existing};
+ my ($first_id,$sfile) = split('_',$first,2);
+ #$main::lxdebug->message(LXDebug->DEBUG2(), "id=".$first_id." sessionfile=". $sfile);
+ my $file = SL::File->get(id => $first_id );
+ $self->js->run('kivi.File.askForRename',$first_id,$file->file_name,$sfile,join (',', @{$self->existing}), $self->is_global);
+ }
+ $self->js->render();
+ } else {
+ $self->render(\$output, { layout => 0, process => 0 });
+ }
+ 1;
+ } or do {
+ if ($json ){
+ $self->js->flash( 'error', t8('internal error (see details)'))
+ ->flash_detail('error', $@)->render;
+ } else {
+ $self->render('generic/error', { layout => 0 }, label_error => $@);
+ }
+ };
+}
+
+
+sub _get_sources {
+ my ($self) = @_;
+ my @sources;
+ $main::lxdebug->message(LXDebug->DEBUG2(), "get_sources file_type=". $self->file_type);
+ if ( $self->file_type eq 'document' ) {
+ ##TODO statt gen neue attribute in filetypes :
+ if (($file_types{$self->object_type}->{gen}*1 & 1)==1) {
+ my $gendata = {
+ 'name' => 'created',
+ 'title' => $main::locale->text('generated Files'),
+ 'chk_action' => 'documents_delete',
+ 'chk_title' => $main::locale->text('Delete Documents'),
+ 'chkall_title' => $main::locale->text('Delete all'),
+ 'file_title' => $main::locale->text('filename'),
+ 'confirm_text' => $main::locale->text('delete'),
+ 'can_rename' => 1,
+ 'rename_title' => $main::locale->text('Rename Documents'),
+ 'done_text' => $main::locale->text('deleted')
+ };
+ push @sources , $gendata;
+ }
+ if (($file_types{$self->object_type}->{gen}*1 & 2)==2) {
+ my @others = SL::File->get_other_sources();
+ $main::lxdebug->message(LXDebug->DEBUG2(), "other cnt=". scalar(@others));
+ foreach my $scanner_or_mailrx (@others) {
+ my $other = {
+ 'name' => $scanner_or_mailrx->{name},
+ 'title' => $main::locale->text('from \'#1\' imported Files',$scanner_or_mailrx->{description}),
+ 'chk_action' => $scanner_or_mailrx->{name}.'_unimport',
+ 'chk_title' => $main::locale->text('Unimport documents'),
+ 'chkall_title' => $main::locale->text('Unimport all'),
+ 'file_title' => $main::locale->text('filename'),
+ 'confirm_text' => $main::locale->text('unimport'),
+ 'can_rename' => 1,
+ 'rename_title' => $main::locale->text('Rename Documents'),
+ 'can_import' => 1,
+ 'import_title' => $main::locale->text('Add Document from \'#1\'',$scanner_or_mailrx->{name}),
+ 'path' => $scanner_or_mailrx->{directory},
+ 'done_text' => $main::locale->text('unimported')
+ };
+ push @sources , $other;
+ }
+ }
+ }
+ elsif ( $self->file_type eq 'attachment' ) {
+ my $attdata = {
+ 'name' => 'uploaded',
+ 'title' => $main::locale->text(''),
+ 'chk_action' => 'attachments_delete',
+ 'chk_title' => $main::locale->text('Delete Attachments'),
+ 'chkall_title' => $main::locale->text('Delete all'),
+ 'file_title' => $main::locale->text('filename'),
+ 'confirm_text' => $main::locale->text('delete'),
+ 'can_rename' => 1,
+ 'are_existing' => $self->existing ? 1 : 0,
+ 'rename_title' => $main::locale->text('Rename Attachments'),
+ 'can_upload' => 1,
+ 'upload_title' => $main::locale->text('Upload Attachments'),
+ 'done_text' => $main::locale->text('deleted')
+ };
+ push @sources , $attdata;
+ }
+ elsif ( $self->file_type eq 'image' ) {
+ my $attdata = {
+ 'name' => 'uploaded',
+ 'title' => $main::locale->text(''),
+ 'chk_action' => 'images_delete',
+ 'chk_title' => $main::locale->text('Delete Images'),
+ 'chkall_title' => $main::locale->text('Delete all'),
+ 'file_title' => $main::locale->text('filename'),
+ 'confirm_text' => $main::locale->text('delete'),
+ 'can_rename' => 1,
+ 'are_existing' => $self->existing ? 1 : 0,
+ 'rename_title' => $main::locale->text('Rename Images'),
+ 'can_upload' => 1,
+ 'upload_title' => $main::locale->text('Upload Images'),
+ 'done_text' => $main::locale->text('deleted')
+ };
+ push @sources , $attdata;
+ }
+ $main::lxdebug->message(LXDebug->DEBUG2(), "get_sources count=".scalar(@sources));
+ return @sources;
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Controller::File - Controller for managing files
+
+
+=head1 SYNOPSIS
+
+=begin text
+
+ # The Controller is called direct from the webpages
+
+
+ <a href="controller.pl?action=File/list&file_type=document\
+ &object_type=[% HTML.escape(type) %]&object_id=[% HTML.url(id) %]">
+
+
+ # or indirect via javascript functions from js/kivi.File.js
+
+
+ kivi.popup_dialog({ url: 'controller.pl',
+ data: { action : 'File/ajax_upload',
+ file_type : 'uploaded',
+ object_type: type,
+ object_id : id
+ }
+ ...
+
+=end text
+
+
+=head1 DESCRIPTION
+
+This is a controller for handling files in a storage independant way.
+The storage may be a Filesystem,a WebDAV, a Database or DMS.
+These backends must be configered in ClientConfig.
+This Controller use as intermediate layer for storage C<SL::File>.
+
+The Controller is responsible to display forms for displaying the files at the ERP-objects and
+for uploading and downloading the files.
+
+More description of the intermediate layer see L<SL::File>.
+
+=head1 METHODS
+
+=head2 C<action_list>
+
+This loads a list of files on a webpage. This can be done with a normal submit or via an ajax/json call.
+Dependant of file_type different sources are available.
+
+For documents there are the 'created' source and the imports from scanners or email.
+For attachments and images only the 'uploaded' source available.
+
+Available C<FORM PARAMS>:
+
+=over 4
+
+=item C<form.object_id>
+
+The Id of the ERP-object.
+
+=item C<form.object_type>
+
+The Type of the ERP-object like "sales_quotation". A clear mapping to the class/model exists in the controller.
+
+=item C<form.file_type>
+
+For one ERP-object may exists different type of documents the type may be "documents","attachments" or "images".
+This file_type is a filter for the list.
+
+=item C<form.json>
+
+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.
+
+=back
+
+
+=head2 C<action_ajax_upload>
+
+
+A new file or more files can selected by a dialog and insert into the system.
+
+
+Available C<FORM PARAMS>:
+
+=over 4
+
+=item C<form.file_type>
+
+This parameter describe here the source for a new file :
+"attachments" and "images"
+
+This is a normal upload selection, which may be more then one file to upload.
+
+=item C<form.object_id>
+
+and
+
+=item C<form.object_type>
+
+are the same as at C<action_list>
+
+=back
+
+=head2 C<action_ajax_files_uploaded>
+
+The Upload of selected Files. The "multipart_formdata" is parsed in SL::Request into the formsvariable "form.ATTACHMENTS".
+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?).
+If the same filename still exists at this object after the download for each existing filename a rename dialog will be opened.
+
+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.
+
+Available C<FORM PARAMS>:
+
+=over 4
+
+=item C<form.ATTACHMENTS.uploadfiles>
+
+This is an array of elements which have {filename} for the name and {data} for the contents.
+
+Also object_id, object_type and file_type
+
+=back
+
+=head2 C<action_download>
+
+This is the real download of a file normally called via javascript "$.download("controller.pl", data);"
+
+Available C<FORM PARAMS>:
+
+=over 4
+
+Also object_id, object_type and file_type
+
+=back
+
+=head2 C<action_ajax_importdialog>
+
+A Dialog with all available and not imported files to import is open.
+More then one file can be selected.
+
+Available C<FORM PARAMS>:
+
+=over 4
+
+=item C<form.source>
+
+The name of the source like "scanner1" or "email"
+
+=item C<form.path>
+
+The full path to the directory on the server, where the files to import can found
+
+Also object_id, object_type and file_type
+
+=back
+
+=head2 C<action_ajax_delete>
+
+Some files can be deleted
+
+Available C<FORM PARAMS>:
+
+=over 4
+
+=item C<form.ids>
+
+The ids of the files to delete. Only this files are deleted not all versions of a file if the exists
+
+=back
+
+=head2 C<action_ajax_unimport>
+
+Some files can be unimported, dependant of the source of the file. This means they are moved
+back to the directory of the source
+
+Available C<FORM PARAMS>:
+
+=over 4
+
+=item C<form.ids>
+
+The ids of the files to unimport. Only this files are unimported not all versions of a file if the exists
+
+=back
+
+=head2 C<action_ajax_rename>
+
+One file can be renamed. There can be some checks if the same filename still exists at one object.
+
+
+=head1 AUTHOR
+
+Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
+
+=cut
+
--- /dev/null
+namespace('kivi.File', function(ns) {
+
+ ns.rename = function(id,type,file_type,checkbox_class,is_global) {
+ var checkboxes = $('.'+checkbox_class).filter(function () { return $(this).prop('checked'); });
+
+ if (checkboxes.size() === 0) {
+ alert(kivi.t8("No file selected, please set one checkbox!"));
+ return false;
+ }
+ if (checkboxes.size() > 1) {
+ alert(kivi.t8("More than one file selected, please set only one checkbox!"));
+ return false;
+ }
+ var file_id = checkboxes[0].value;
+ $('#newfilename_id').val($('#filename_'+file_id).text());
+ $('#next_ids_id').val('');
+ $('#is_global_id').val(is_global);
+ $('#rename_id_id').val(file_id);
+ $('#sessionfile_id').val('');
+ $('#rename_extra_text').html('');
+ kivi.popup_dialog({
+ id: 'rename_dialog',
+ dialog: { title: kivi.t8("Rename attachment")
+ , width: 400
+ , height: 200
+ , modal: true } });
+ return true;
+ }
+
+ ns.renameclose = function() {
+ $("#rename_dialog").dialog('close');
+ return false;
+ }
+
+ ns.renameaction = function() {
+ $("#rename_dialog").dialog('close');
+ var data = {
+ action: 'File/ajax_rename',
+ id: $('#rename_id_id').val(),
+ to: $('#newfilename_id').val(),
+ next_ids: $('#next_ids_id').val(),
+ is_global: $('#is_global_id').val(),
+ sessionfile: $('#sessionfile_id').val(),
+ };
+ $.post("controller.pl", data, kivi.eval_json_result);
+ return true;
+ }
+
+ ns.askForRename = function(file_id,file_name,sessionfile,next_ids,is_global) {
+ $('#newfilename_id').val(file_name);
+ $('#rename_id_id').val(file_id);
+ $('#is_global_id').val(is_global);
+ $('#next_ids_id').val(next_ids);
+ $('#sessionfile_id').val(sessionfile);
+ $('#rename_extra_text').html(kivi.t8("The uploaded filename still exists.<br>If you not modify the name this is a new version of the file"));
+ kivi.popup_dialog(
+ {
+ id: 'rename_dialog',
+ dialog: { title: kivi.t8("Rename attachment")
+ , width: 400
+ , height: 200
+ , modal: true }
+ });
+ }
+
+ ns.upload = function(id,type,filetype,upload_title,gl) {
+ kivi.popup_dialog({ url: 'controller.pl',
+ data: { action: 'File/ajax_upload',
+ file_type: filetype,
+ object_type: type,
+ object_id: id,
+ is_global: gl
+ },
+ id: 'files_upload',
+ dialog: { title: upload_title, width: 650, height: 240 } });
+ return true;
+ }
+
+ ns.reset_upload_form = function() {
+ $('#attachment_updfile').val('');
+ $("#upload_result").html('');
+ ns.allow_upload_submit();
+ }
+
+ ns.allow_upload_submit = function() {
+ $('#upload_selected_button').prop('disabled',$('#upload_files').val() === '');
+ }
+
+ ns.upload_selected_files = function(id,type,filetype,maxsize,is_global) {
+ var myform = document.getElementById("upload_form");
+ var filesize = 0;
+ var myfiles = document.getElementById("upload_files").files;
+ for ( i=0; i < myfiles.length; i++ ) {
+ var fname ='';
+ try {
+ filesize += myfiles[i].size;
+ fname = encodeURIComponent(myfiles[i].name);
+ }
+ catch(err) {
+ fname ='';
+ try {
+ fname = myfiles[i].name;
+ }
+ catch(err2) { fname ='';}
+ $("#upload_result").html(kivi.t8("filename has not uploadable characters ")+fname);
+ return;
+ }
+ }
+ if ( filesize > maxsize ) {
+ $("#upload_result").html(kivi.t8("filesize too big: ")+
+ filesize+ kivi.t8(" bytes, max=") + maxsize );
+ return;
+ }
+
+ myform.action ="controller.pl?action=File/ajax_files_uploaded&json=1&object_type="+
+ type+'&object_id='+id+'&file_type='+filetype+'&is_global='+is_global;
+ var oReq = new XMLHttpRequest();
+ oReq.onload = ns.attSuccess;
+ oReq.upload.onprogress = ns.attProgress;
+ oReq.upload.onerror = ns.attFailed;
+ oReq.upload.onabort = ns.attCanceled;
+ oReq.open("post",myform.action, true);
+ $("#upload_result").html(kivi.t8("start upload"));
+ oReq.send(new FormData(myform));
+ }
+
+ ns.attProgress = function(oEvent) {
+ if (oEvent.lengthComputable) {
+ var percentComplete = (oEvent.loaded / oEvent.total) * 100;
+ $("#upload_result").html(percentComplete+" % "+ kivi.t8("uploaded"));
+ }
+ }
+
+ ns.attFailed = function(evt) {
+ $('#files_upload').dialog('close');
+ $("#upload_result").html(kivi.t8("An error occurred while transferring the file."));
+ }
+
+ ns.attCanceled = function(evt) {
+ $('#files_upload').dialog('close');
+ $("#upload_result").html(kivi.t8("The transfer has been canceled by the user."));
+ }
+
+ ns.attSuccess = function() {
+ $('#files_upload').dialog('close');
+ kivi.eval_json_result(jQuery.parseJSON(this.response));
+ }
+
+ ns.delete = function(id,type,file_type,checkbox_class,is_global) {
+ var checkboxes = $('.'+checkbox_class).filter(function () { return $(this).prop('checked'); });
+
+ if ((checkboxes.size() === 0) ||
+ !confirm(kivi.t8('Do you really want to delete the selected documents?')))
+ return false;
+ var data = {
+ action : 'File/ajax_delete',
+ object_id : id,
+ object_type: type,
+ file_type : file_type,
+ ids : checkbox_class,
+ is_global : is_global,
+ };
+ $.post("controller.pl?" + checkboxes.serialize(), data, kivi.eval_json_result);
+ return false;
+ }
+
+ ns.unimport = function(id,type,file_type,checkbox_class) {
+ var checkboxes = $('.'+checkbox_class).filter(function () { return $(this).prop('checked'); });
+
+ if ((checkboxes.size() === 0) ||
+ !confirm(kivi.t8('Do you really want to unimport the selected documents?')))
+ return false;
+ var data = {
+ action : 'File/ajax_unimport',
+ object_id : id,
+ object_type: type,
+ file_type : file_type,
+ ids : checkbox_class,
+ };
+ $.post("controller.pl?" + checkboxes.serialize(), data, kivi.eval_json_result);
+ return false;
+ }
+
+ ns.update = function(id,type,file_type,is_global) {
+ var data = {
+ action: 'File/list',
+ json: 1,
+ object_type: type,
+ object_id: id,
+ file_type: file_type,
+ is_global: is_global
+ };
+
+ $.post("controller.pl", data, kivi.eval_json_result);
+ return false;
+ }
+
+ ns.import = function (id,type,file_type,fromwhere,frompath) {
+ kivi.popup_dialog({ url: 'controller.pl',
+ data: { action : 'File/ajax_importdialog',
+ object_type : type,
+ source : fromwhere,
+ path : frompath,
+ file_type : file_type,
+ object_id : id
+ },
+ id: 'import_dialog',
+ dialog: { title: kivi.t8('Import documents from #1',[fromwhere]), width: 420, height: 540 }
+ });
+ return true;
+ }
+
+ ns.importclose = function() {
+ $("#import_dialog").dialog('close');
+ return false;
+ }
+
+ ns.importaction = function(id,type,file_type,fromwhere,frompath,checkbox_class) {
+ var checkboxes = $('.'+checkbox_class).filter(function () { return $(this).prop('checked'); });
+
+ $("#import_dialog").dialog('close');
+ if (checkboxes.size() === 0) {
+ return false;
+ }
+ var data = {
+ action : 'File/ajax_import',
+ object_id : id,
+ object_type: type,
+ file_type : file_type,
+ source : fromwhere,
+ path : frompath,
+ ids : checkbox_class
+ };
+ $.post("controller.pl?" + checkboxes.serialize(), data, kivi.eval_json_result);
+ return true;
+ }
+
+
+ ns.init = function() {
+ }
+
+});