Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 0bfbcce6

Von Martin Helmling martin.helmling@octosoft.eu vor fast 8 Jahren hinzugefügt

  • ID 0bfbcce6e77e0f9d83e4f54f3fe9da7edcc866f3
  • Vorgänger 1ce68041
  • Nachfolger 54ce5144

Dateimanagement: Controller zum Laden und Generierung der Dateien

sowie die dazugehörenden Templates

Unterschiede anzeigen:

SL/Controller/File.pm
1
package SL::Controller::File;
2

  
3
use strict;
4

  
5
use parent qw(SL::Controller::Base);
6

  
7
use List::Util qw(first max);
8

  
9
use utf8;
10
use Encode qw(decode);
11
use URI::Escape;
12
use Cwd;
13
use DateTime;
14
use File::stat;
15
use File::Spec::Unix;
16
use File::Spec::Win32;
17
use File::MimeInfo::Magic;
18
use SL::DB::Helper::Mappings;
19
use SL::DB::Order;
20
use SL::DB::DeliveryOrder;
21
use SL::DB::Invoice;
22

  
23
use SL::DB::PurchaseInvoice;
24
use SL::DB::Part;
25
use SL::DB::GLTransaction;
26
use SL::DB::Draft;
27
use SL::DB::History;
28
use SL::JSON;
29
use SL::Helper::CreatePDF qw(:all);
30
use SL::Locale::String;
31
use SL::SessionFile;
32
use SL::File;
33
use SL::Controller::Helper::ThumbnailCreator qw(file_probe_image_type);
34

  
35
use constant DO_DELETE    =>   0;
36
use constant DO_UNIMPORT  =>   1;
37

  
38

  
39
use Rose::Object::MakeMethods::Generic
40
(
41
    'scalar --get_set_init' => [ qw() ],
42
    'scalar' => [ qw(object object_type object_model object_id object_right file_type files is_global existing) ],
43
);
44

  
45
__PACKAGE__->run_before('check_object_params', only => [ qw(list ajax_delete ajax_importdialog ajax_import ajax_unimport ajax_upload ajax_files_uploaded) ]);
46

  
47
my %file_types = (
48
  'sales_quotation' =>         { gen => 1 ,gltype => '',   dir =>'SalesQuotation',       model => 'Order',          right => 'import_ar'  },
49
  'sales_order'     =>         { gen => 1 ,gltype => '',   dir =>'SalesOrder',           model => 'Order',          right => 'import_ar'  },
50
  'sales_delivery_order' =>    { gen => 1 ,gltype => '',   dir =>'SalesDeliveryOrder',   model => 'DeliveryOrder',  right => 'import_ar'  },
51
  'invoice'         =>         { gen => 1 ,gltype => 'ar', dir =>'SalesInvoice',         model => 'Invoice',        right => 'import_ar'  },
52
  'credit_note'     =>         { gen => 1 ,gltype => '',   dir =>'CreditNote',           model => 'Invoice',        right => 'import_ar'  },
53
  'request_quotation' =>       { gen => 3 ,gltype => '',   dir =>'RequestForQuotation',  model => 'Order',          right => 'import_ap'  },
54
  'purchase_order' =>          { gen => 3 ,gltype => '',   dir =>'PurchaseOrder',        model => 'Order',          right => 'import_ap'  },
55
  'purchase_delivery_order' => { gen => 3 ,gltype => '',   dir =>'PurchaseDeliveryOrder',model => 'DeliveryOrder',  right => 'import_ap'  },
56
  'purchase_invoice' =>        { gen => 2 ,gltype => 'ap', dir =>'PurchaseInvoice',      model => 'PurchaseInvoice',right => 'import_ap'  },
57
  'vendor' =>                  { gen => 0 ,gltype => '',   dir =>'Vendor',               model => 'Vendor',         right => 'xx'  },
58
  'customer' =>                { gen => 1 ,gltype => '',   dir =>'Customer',             model => 'Customer',       right => 'xx'  },
59
  'part' =>                    { gen => 0 ,gltype => '',   dir =>'Part',                 model => 'Part',           right => 'xx'  },
60
  'gl_transaction' =>          { gen => 2 ,gltype => 'gl', dir =>'GeneralLedger',        model => 'GLTransaction',  right => 'import_ap'  },
61
  'draft' =>                   { gen => 0 ,gltype => '',   dir =>'Draft',                model => 'Draft',          right => 'xx'  },
62
  'csv_customer' =>            { gen => 1 ,gltype => '',   dir =>'Reports',              model => 'Customer',       right => 'xx'  },
63
  'csv_vendor'   =>            { gen => 1 ,gltype => '',   dir =>'Reports',              model => 'Vendor',         right => 'xx'  },
64
);
65

  
66
#--- 4 locale ---#
67
# $main::locale->text('imported')
68

  
69
#
70
# actions
71
#
72

  
73
sub action_list {
74
  my ($self) = @_;
75

  
76
  my $isjson = 0;
77
  $isjson = 1 if $::form->{json};
78

  
79
  $self->_do_list($isjson);
80
}
81

  
82
sub action_ajax_importdialog {
83
  my ($self) = @_;
84
  $::auth->assert($self->object_right);
85
  my $path   = $::form->{path};
86
  my @files  = $self->_get_from_import($path);
87
  my $source = {
88
    'name'         => $::form->{source},
89
    'path'         => $path ,
90
    'chk_action'   => $::form->{source}.'_import',
91
    'chk_title'    => $main::locale->text('Import scanned documents'),
92
    'chkall_title' => $main::locale->text('Import all'),
93
    'files'        => \@files
94
  };
95
  $self->render('file/import_dialog',
96
                { layout => 0
97
                },
98
                source => $source
99
  );
100
}
101

  
102
sub action_ajax_import {
103
  my ($self) = @_;
104
  $::auth->assert($self->object_right);
105
  my $ids    = $::form->{ids};
106
  my $source = $::form->{source};
107
  my $path   = $::form->{path};
108
  my @files = $self->_get_from_import($path);
109
  foreach my $filename (@{ $::form->{$ids} || [] }) {
110
    my ($file,undef) = grep { $_->{name} eq $filename } @files;
111
    if ( $file ) {
112
      my $obj = SL::File->save(object_id     => $self->object_id,
113
                               object_type   => $self->object_type,
114
                               mime_type     => 'application/pdf',
115
                               source        => $source,
116
                               file_type     => 'document',
117
                               file_name     => $file->{filename},
118
                               file_path     => $file->{path}
119
                             );
120
      unlink($file->{path}) if $obj;
121
    }
122
  }
123
  $self->_do_list(1);
124
}
125

  
126
sub action_ajax_delete {
127
  my ($self) = @_;
128
  $self->_delete_all(DO_DELETE,$::locale->text('Following files are deleted:'));
129
}
130

  
131
sub action_ajax_unimport {
132
  my ($self) = @_;
133
  $self->_delete_all(DO_UNIMPORT,$::locale->text('Following files are unimported:'));
134
}
135

  
136
sub action_ajax_rename {
137
  my ($self) = @_;
138
  my $file = SL::File->get(id => $::form->{id});
139
  if ( ! $file ) {
140
    $self->js->flash('error',$::locale->text('File not exists !'))->render();
141
    return;
142
  }
143
  $main::lxdebug->message(LXDebug->DEBUG2(), "object_id=".$file->object_id." object_type=".$file->object_type." dbfile=".$file);
144
  my $sessionfile = $::form->{sessionfile};
145
      $main::lxdebug->message(LXDebug->DEBUG2(), "sessionfile=".$sessionfile);
146
  if ( $sessionfile && -f $sessionfile ) {
147
     $main::lxdebug->message(LXDebug->DEBUG2(), "file=".$file->file_name." to=".$::form->{to}." sessionfile=".$sessionfile);
148
    # new uploaded file
149
    if ( $::form->{to} eq $file->file_name ) {
150
      # no rename so use as new version
151
      $file->save_file($sessionfile);
152
      $self->js->flash('warning',$::locale->text('File \'#1\' is used as new Version !',$file->file_name));
153

  
154
    } else {
155
      # new filename so it is a new file with same attributes as old file
156
      eval {
157
        SL::File->save(object_id     => $file->object_id,
158
                       object_type   => $file->object_type,
159
                       mime_type     => $file->mime_type,
160
                       source        => $file->source,
161
                       file_type     => $file->file_type,
162
                       file_name     => $::form->{to},
163
                       file_path     => $sessionfile
164
                     );
165
        unlink($sessionfile);
166
        1;
167
      } or do {
168
        $self->js->flash(       'error', t8('internal error (see details)'))
169
                 ->flash_detail('error', $@)->render;
170
        return;
171
      }
172
    }
173

  
174
  } else {
175
    # normal rename
176
    eval {
177
      my $res = $file->rename($::form->{to});
178
      $main::lxdebug->message(LXDebug->DEBUG2(), "rename result=".$res);
179
      if ($res > SL::File::RENAME_OK) {
180
        $self->js->flash('error',
181
                         $res == SL::File::RENAME_EXISTS      ? $::locale->text('File still exists !')
182
                       : $res == SL::File::RENAME_SAME        ? $::locale->text('Same Filename !')
183
                       :                                        $::locale->text('File not exists !'))->render;
184
        return 1;
185
      }
186
      1;
187
    } or do {
188
      $self->js->flash(       'error', t8('internal error (see details)'))
189
               ->flash_detail('error', $@)->render;
190
      return;
191
    }
192
  }
193
  $self->is_global($::form->{is_global});
194
  $self->file_type(  $file->file_type);
195
  $self->object_type($file->object_type);
196
  $self->object_id(  $file->object_id);
197
  #$self->object_model($file_types{$file->module}->{model});
198
  #$self->object_right($file_types{$file->module}->{right});
199
  if ( $::form->{next_ids} ) {
200
    my @existing = split(/,/, $::form->{next_ids});
201
    $self->existing(\@existing);
202
  }
203
  $self->_do_list(1);
204
}
205

  
206
sub action_ajax_upload {
207
  my ($self) = @_;
208
  $self->{maxsize} = $::instance_conf->get_doc_max_filesize;
209
  $self->{accept_types} = '';
210
  $self->{accept_types} = 'image/png,image/gif,image/jpeg,image/tiff,*png,*gif,*.jpg,*.tif' if $self->{file_type} eq 'image';
211
  $self->render('file/upload_dialog',
212
                { layout          => 0
213
                },
214
  );
215
}
216

  
217
sub action_ajax_files_uploaded {
218
  my ($self) = @_;
219

  
220
  my $source = 'uploaded';
221
  $main::lxdebug->message(LXDebug->DEBUG2(), "file_upload UPLOAD=".$::form->{ATTACHMENTS}->{uploadfiles});
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
        $main::lxdebug->message(LXDebug->DEBUG2(), "file_upload name=".$fname);
229
        ## normalize and find basename
230
        # first split with unix rules
231
        # after that split with windows rules
232
        my ($volume,$directories,$basefile) = File::Spec::Unix->splitpath($fname);
233
        ($volume,$directories,$basefile) = File::Spec::Win32->splitpath($basefile);
234

  
235
        # to find real mime_type by magic we must save the filedata
236

  
237
        my $sess_fname = "file_upload_".$self->object_type."_".$self->object_id."_".$idx;
238
        my $sfile     = SL::SessionFile->new($sess_fname, mode => 'w');
239

  
240
        $sfile->fh->print(${$upfiles[$idx]->{data}});
241
        $sfile->fh->close;
242
        my $mime_type = File::MimeInfo::Magic::magic($sfile->file_name);
243

  
244
        if (! $mime_type) {
245
          # if filename has the suffix "pdf", but is really no pdf set mimetype for no suffix
246
          $mime_type = File::MimeInfo::Magic::mimetype($basefile);
247
          $mime_type = 'application/octet-stream' if $mime_type eq 'application/pdf' || !$mime_type;
248
        }
249
        $main::lxdebug->message(LXDebug->DEBUG2(), "mime_type=".$mime_type);
250
        if ( $self->file_type eq 'image' && $self->file_probe_image_type($mime_type, $basefile)) {
251
          next;
252
        }
253
        my ($existobj) = SL::File->get_all(object_id     => $self->object_id,
254
                                        object_type   => $self->object_type,
255
                                        mime_type     => $mime_type,
256
                                        source        => $source,
257
                                        file_type     => $self->file_type,
258
                                        file_name     => $basefile,
259
                                      );
260

  
261
        $main::lxdebug->message(LXDebug->DEBUG2(), "store1 exist=".$existobj);
262
        if ($existobj) {
263
  $main::lxdebug->message(LXDebug->DEBUG2(), "id=".$existobj->id." sessionfile=". $sfile->file_name);
264
          push @existing, $existobj->id.'_'.$sfile->file_name;
265
        } else {
266
          my $fileobj = SL::File->save(object_id     => $self->object_id,
267
                                       object_type   => $self->object_type,
268
                                       mime_type     => $mime_type,
269
                                       source        => $source,
270
                                       file_type     => $self->file_type,
271
                                       file_name     => $basefile,
272
                                       ## two possibilities: what is better ? content or sessionfile ??
273
                                       #file_contents => ${$upfiles[$idx]->{data}},
274
                                       file_path     => $sfile->file_name
275
                                     );
276
          $main::lxdebug->message(LXDebug->DEBUG2(), "obj=".$fileobj);
277
          unlink($sfile->file_name);
278
        }
279
        1;
280
      } or do {
281
        $self->js->flash(       'error', t8('internal error (see details)'))
282
                 ->flash_detail('error', $@)->render;
283
        return;
284
      }
285
    }
286
  }
287
  $self->existing(\@existing);
288
  $self->_do_list(1);
289
}
290

  
291
sub action_download {
292
  my ($self) = @_;
293
  my ($id,$version) = split /_/, $::form->{id};
294
  my $file = SL::File->get(id => $id );
295
  $file->version($version) if $version;
296
  my $ref  = $file->get_content;
297
  if ( $file && $ref ) {
298
    return $self->send_file($ref,
299
      type => $file->mime_type,
300
      name => $file->file_name,
301
    );
302
  }
303
}
304

  
305
#
306
# filters
307
#
308

  
309
sub check_object_params {
310
  my ($self) = @_;
311

  
312
  my $id = $::form->{object_id} +0;
313
  my $draftid = $::form->{draft_id} +0;
314
  my $gldoc = 0;
315
  my $type = undef;
316

  
317
  if ( $draftid == 0 && $id == 0 && $::form->{is_global} ) {
318
    $gldoc = 1;
319
    $type = $::form->{object_type};
320
  }
321
  elsif ( $id == 0 ) {
322
    $id = $::form->{draft_id};
323
    $type = 'draft';
324
  } elsif ( $::form->{object_type} ) {
325
    $type = $::form->{object_type};
326
  }
327
  die "No object type"     if ! $type;
328
  die "No file type"       if ! $::form->{file_type};
329
  die "Unkown object type" if ! $file_types{$type};
330

  
331
  $self->is_global($gldoc);
332
  $self->file_type($::form->{file_type});
333
  $self->object_type($type);
334
  $self->object_id($id);
335
  $self->object_model($file_types{$type}->{model});
336
  $self->object_right($file_types{$type}->{right});
337
  $main::lxdebug->message(LXDebug->DEBUG2(), "checked: object_id=".$self->object_id." object_type=".$self->object_type." is_global=".$self->is_global);
338

  
339
 # $::auth->assert($self->object_right);
340

  
341
 # my $model = 'SL::DB::' . $self->object_model;
342
 # $self->object($model->new(id => $self->object_id)->load || die "Record not found");
343

  
344
  return 1;
345
}
346

  
347
#
348
# private methods
349
#
350

  
351
sub _delete_all {
352
  my ($self,$do_unimport,$infotext) = @_;
353
  my $files = '';
354
  my $ids = $::form->{ids};
355
  foreach my $id_version (@{ $::form->{$ids} || [] }) {
356
    my ($id,$version) = split /_/, $id_version;
357
    my $dbfile = SL::File->get(id => $id);
358
    $dbfile->version($version) if $dbfile && $version;
359
    if ( $dbfile && $dbfile->delete ) {
360
      $files .= ' '.$dbfile->file_name;
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
  $main::lxdebug->message(LXDebug->DEBUG2(), "do_list: object_id=".$self->object_id." object_type=".$self->object_type." file_type=".$self->file_type." json=".$json);
371
  if ( $self->file_type eq 'document' ) {
372
    @files   = SL::File->get_all_versions(object_id   => $self->object_id  ,
373
                                          object_type => $self->object_type,
374
                                          file_type   => $self->file_type  );
375

  
376
    $main::lxdebug->message(LXDebug->DEBUG2(), "cnt1=".scalar(@files));
377
  }
378
  elsif ( $self->file_type eq 'attachment' ||  $self->file_type eq 'image' ) {
379
    @files   = SL::File->get_all(object_id   => $self->object_id  ,
380
                                 object_type => $self->object_type,
381
                                 file_type   => $self->file_type  );
382
    $main::lxdebug->message(LXDebug->DEBUG2(), "cnt2=".scalar(@files));
383
  }
384
  $self->files(\@files);
385
  $self->_mk_render('file/list',1,0,$json);
386
}
387

  
388
sub _get_from_import {
389
  my ($self,$path) = @_;
390
  my @foundfiles ;
391

  
392
  $main::lxdebug->message(LXDebug->DEBUG2(), "import path=".$path);
393
  my $language = $::lx_office_conf{system}->{language};
394
  my $timezone = $::locale->get_local_time_zone()->name;
395
  if (opendir my $dir, $path) {
396
    my @files = ( readdir $dir);
397
    foreach my $file ( @files) {
398
      next if (($file eq '.') || ($file eq '..'));
399
      $file = Encode::decode('utf-8', $file);
400
      $main::lxdebug->message(LXDebug->DEBUG2(), "file=".$file);
401

  
402
      next if( -d "$path/$file");
403

  
404
      my $tmppath = File::Spec->catfile( $path, $file );
405
      $main::lxdebug->message(LXDebug->DEBUG2(), "tmppath=".$tmppath." file=".$file);
406
      next if( ! -f $tmppath);
407

  
408
      my $st = stat($tmppath);
409
      my $dt = DateTime->from_epoch( epoch => $st->mtime, time_zone => $timezone, locale => $language);
410
      my $sname = $main::locale->quote_special_chars('HTML',$file);
411
      push @foundfiles , {
412
        'name'     => $file,
413
        'filename' => $sname,
414
        'path'     => $tmppath,
415
        'mtime'    => $st->mtime,
416
        'date'     => $dt->dmy('.')." ".$dt->hms,
417
      };
418

  
419
    }
420
  }
421
  $main::lxdebug->message(LXDebug->DEBUG2(), "return ".scalar(@foundfiles)." files");
422
  return @foundfiles;
423
}
424

  
425
sub _mk_render {
426
  my ($self,$template,$edit,$scanner,$json) = @_;
427
  my $err;
428
  eval {
429
    ##TODO  here a configurable code must be implemented
430

  
431
    my $title;
432
    $main::lxdebug->message(LXDebug->DEBUG2(), "mk_render: object_id=".$self->object_id." object_type=".$self->object_type.
433
                              " file_type=".$self->file_type." json=".$json." filecount=".scalar(@{ $self->files })." is_global=".$self->is_global);
434
    my @sources = $self->_get_sources();
435
    foreach my $source ( @sources ) {
436
      $main::lxdebug->message(LXDebug->DEBUG2(), "mk_render: source name=".$source->{name});
437
      @{$source->{files}} = grep { $_->source eq $source->{name}} @{ $self->files };
438
    }
439
    if ( $self->file_type eq 'document' ) {
440
      $title = $main::locale->text('Documents');
441
    } elsif ( $self->file_type eq 'attachment' ) {
442
      $title = $main::locale->text('Attachments');
443
    } elsif ( $self->file_type eq 'image' ) {
444
      $title = $main::locale->text('Images');
445
    }
446

  
447
    my $output         = SL::Presenter->get->render(
448
      $template,
449
      title             => $title,
450
      SOURCES           => \@sources,
451
      edit_attachments  => $edit,
452
      object_type       => $self->object_type,
453
      object_id         => $self->object_id,
454
      file_type         => $self->file_type,
455
      is_global         => $self->is_global,
456
      json              => $json,
457
    );
458
    if ( $json ) {
459
      $self->js->html('#'.$self->file_type.'_list_'.$self->object_type, $output);
460
      if ( $self->existing && scalar(@{$self->existing}) > 0) {
461
        my $first = shift @{$self->existing};
462
        my ($first_id,$sfile) = split('_',$first,2);
463
        #$main::lxdebug->message(LXDebug->DEBUG2(), "id=".$first_id." sessionfile=". $sfile);
464
        my $file = SL::File->get(id => $first_id );
465
        $self->js->run('kivi.File.askForRename',$first_id,$file->file_name,$sfile,join (',', @{$self->existing}), $self->is_global);
466
      }
467
      $self->js->render();
468
    } else {
469
        $self->render(\$output, { layout => 0, process => 0 });
470
    }
471
    1;
472
  } or do {
473
    if ($json ){
474
      $self->js->flash(       'error', t8('internal error (see details)'))
475
               ->flash_detail('error', $@)->render;
476
    } else {
477
      $self->render('generic/error', { layout => 0 }, label_error => $@);
478
    }
479
  };
480
}
481

  
482

  
483
sub _get_sources {
484
  my ($self) = @_;
485
  my @sources;
486
  $main::lxdebug->message(LXDebug->DEBUG2(), "get_sources file_type=". $self->file_type);
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_rename'   => 1,
499
        'rename_title' => $main::locale->text('Rename Documents'),
500
        'done_text'    => $main::locale->text('deleted')
501
      };
502
      push @sources , $gendata;
503
    }
504
    if (($file_types{$self->object_type}->{gen}*1 & 2)==2) {
505
      my @others =  SL::File->get_other_sources();
506
      $main::lxdebug->message(LXDebug->DEBUG2(), "other cnt=". scalar(@others));
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
          'import_title' => $main::locale->text('Add Document from \'#1\'',$scanner_or_mailrx->{name}),
520
          'path'         => $scanner_or_mailrx->{directory},
521
          'done_text'    => $main::locale->text('unimported')
522
        };
523
        push @sources , $other;
524
      }
525
    }
526
  }
527
  elsif ( $self->file_type eq 'attachment' ) {
528
    my $attdata = {
529
      'name'         => 'uploaded',
530
      'title'        => $main::locale->text(''),
531
      'chk_action'   => 'attachments_delete',
532
      'chk_title'    => $main::locale->text('Delete Attachments'),
533
      'chkall_title' => $main::locale->text('Delete all'),
534
      'file_title'   => $main::locale->text('filename'),
535
      'confirm_text' => $main::locale->text('delete'),
536
      'can_rename'   => 1,
537
      'are_existing' => $self->existing ? 1 : 0,
538
      'rename_title' => $main::locale->text('Rename Attachments'),
539
      'can_upload'   => 1,
540
      'upload_title' => $main::locale->text('Upload Attachments'),
541
      'done_text'    => $main::locale->text('deleted')
542
    };
543
    push @sources , $attdata;
544
  }
545
  elsif ( $self->file_type eq 'image' ) {
546
    my $attdata = {
547
      'name'         => 'uploaded',
548
      'title'        => $main::locale->text(''),
549
      'chk_action'   => 'images_delete',
550
      'chk_title'    => $main::locale->text('Delete Images'),
551
      'chkall_title' => $main::locale->text('Delete all'),
552
      'file_title'   => $main::locale->text('filename'),
553
      'confirm_text' => $main::locale->text('delete'),
554
      'can_rename'   => 1,
555
      'are_existing' => $self->existing ? 1 : 0,
556
      'rename_title' => $main::locale->text('Rename Images'),
557
      'can_upload'   => 1,
558
      'upload_title' => $main::locale->text('Upload Images'),
559
      'done_text'    => $main::locale->text('deleted')
560
    };
561
    push @sources , $attdata;
562
  }
563
  $main::lxdebug->message(LXDebug->DEBUG2(), "get_sources count=".scalar(@sources));
564
  return @sources;
565
}
566

  
567
1;
568

  
569
__END__
570

  
571
=pod
572

  
573
=encoding utf-8
574

  
575
=head1 NAME
576

  
577
SL::Controller::File - Controller for managing files
578

  
579

  
580
=head1 SYNOPSIS
581

  
582
=begin text
583

  
584
    # The Controller is called direct from the webpages
585

  
586

  
587
    <a href="controller.pl?action=File/list&file_type=document\
588
       &object_type=[% HTML.escape(type) %]&object_id=[% HTML.url(id) %]">
589

  
590

  
591
    # or indirect via javascript functions from js/kivi.File.js
592

  
593

  
594
    kivi.popup_dialog({ url:     'controller.pl',
595
                        data:    { action     : 'File/ajax_upload',
596
                                   file_type  : 'uploaded',
597
                                   object_type: type,
598
                                   object_id  : id
599
                                 }
600
                           ...
601

  
602
=end text
603

  
604

  
605
=head1 DESCRIPTION
606

  
607
This is a controller for handling files in a storage independant way.
608
The storage may be a Filesystem,a WebDAV, a Database or DMS.
609
These backends must be configered in ClientConfig.
610
This Controller use as intermediate layer for storage C<SL::File>.
611

  
612
The Controller is responsible to display forms for displaying the files at the ERP-objects and
613
for uploading and downloading the files.
614

  
615
More description of the intermediate layer see L<SL::File>.
616

  
617
=head1 METHODS
618

  
619
=head2 C<action_list>
620

  
621
This loads a list of files on a webpage. This can be done with a normal submit or via an ajax/json call.
622
Dependant of file_type different sources are available.
623

  
624
For documents there are the 'created' source and the imports from scanners or email.
625
For attachments and images only the 'uploaded' source available.
626

  
627
Available C<FORM PARAMS>:
628

  
629
=over 4
630

  
631
=item C<form.object_id>
632

  
633
The Id of the ERP-object.
634

  
635
=item C<form.object_type>
636

  
637
The Type of the ERP-object like "sales_quotation". A clear mapping to the class/model exists in the controller.
638

  
639
=item C<form.file_type>
640

  
641
For one ERP-object may exists different type of documents the type may be "documents","attachments" or "images".
642
This file_type is a filter for the list.
643

  
644
=item C<form.json>
645

  
646
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.
647

  
648
=back
649

  
650

  
651
=head2 C<action_ajax_upload>
652

  
653

  
654
A new file or more files can selected by a dialog and insert into the system.
655

  
656

  
657
Available C<FORM PARAMS>:
658

  
659
=over 4
660

  
661
=item C<form.file_type>
662

  
663
This parameter describe here the source for a new file :
664
"attachments" and "images"
665

  
666
This is a normal upload selection, which may be more then one file to upload.
667

  
668
=item C<form.object_id>
669

  
670
and
671

  
672
=item C<form.object_type>
673

  
674
are the same as at C<action_list>
675

  
676
=back
677

  
678
=head2  C<action_ajax_files_uploaded>
679

  
680
The Upload of selected Files. The "multipart_formdata" is parsed in SL::Request into the formsvariable "form.ATTACHMENTS".
681
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?).
682
If the same filename still exists at this object after the download for each existing filename a rename dialog will be opened.
683

  
684
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.
685

  
686
Available C<FORM PARAMS>:
687

  
688
=over 4
689

  
690
=item C<form.ATTACHMENTS.uploadfiles>
691

  
692
This is an array of elements which have {filename} for the name and {data} for the contents.
693

  
694
Also object_id, object_type and file_type
695

  
696
=back
697

  
698
=head2 C<action_download>
699

  
700
This is the real download of a file normally called via javascript "$.download("controller.pl", data);"
701

  
702
Available C<FORM PARAMS>:
703

  
704
=over 4
705

  
706
Also object_id, object_type and file_type
707

  
708
=back
709

  
710
=head2 C<action_ajax_importdialog>
711

  
712
A Dialog with all available and not imported files to import is open.
713
More then one file can be selected.
714

  
715
Available C<FORM PARAMS>:
716

  
717
=over 4
718

  
719
=item C<form.source>
720

  
721
The name of the source like "scanner1" or "email"
722

  
723
=item C<form.path>
724

  
725
The full path to the directory on the server, where the files to import can found
726

  
727
Also object_id, object_type and file_type
728

  
729
=back
730

  
731
=head2 C<action_ajax_delete>
732

  
733
Some files can be deleted
734

  
735
Available C<FORM PARAMS>:
736

  
737
=over 4
738

  
739
=item C<form.ids>
740

  
741
The ids of the files to delete. Only this files are deleted not all versions of a file if the exists
742

  
743
=back
744

  
745
=head2 C<action_ajax_unimport>
746

  
747
Some files can be unimported, dependant of the source of the file. This means they are moved
748
back to the directory of the source
749

  
750
Available C<FORM PARAMS>:
751

  
752
=over 4
753

  
754
=item C<form.ids>
755

  
756
The ids of the files to unimport. Only this files are unimported not all versions of a file if the exists
757

  
758
=back
759

  
760
=head2 C<action_ajax_rename>
761

  
762
One file can be renamed. There can be some checks if the same filename still exists at one object.
763

  
764

  
765
=head1 AUTHOR
766

  
767
Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
768

  
769
=cut
770

  
SL/Controller/Helper/ThumbnailCreator.pm
1
package SL::Controller::Helper::ThumbnailCreator;
2

  
3
use strict;
4

  
5
use SL::Locale::String qw(t8);
6
use Carp;
7
use GD;
8
use Image::Info;
9
use File::MimeInfo::Magic;
10
use List::MoreUtils qw(apply);
11
use List::Util qw(max);
12
use Rose::DB::Object::Util;
13

  
14
require Exporter;
15
our @ISA      = qw(Exporter);
16
our @EXPORT   = qw(file_create_thumbnail file_update_thumbnail file_probe_type file_probe_image_type file_update_type_and_dimensions);
17

  
18
# TODO PDFs and others like odt,txt,...
19
our %supported_mime_types = (
20
  'image/gif'  => { extension => 'gif', convert_to_png => 1, },
21
  'image/png'  => { extension => 'png' },
22
  'image/jpeg' => { extension => 'jpg' },
23
  'image/tiff' => { extension => 'tif'},
24
);
25

  
26
sub file_create_thumbnail {
27
  my ($self) = @_;
28
  croak "No picture set yet" if !$self->file_content;
29

  
30
  my $image            = GD::Image->new($self->file_content);
31
  my ($width, $height) = $image->getBounds;
32
  my $max_dim          = 64;
33
  my $curr_max         = max $width, $height, 1;
34
  my $factor           = $curr_max <= $max_dim ? 1 : $curr_max / $max_dim;
35
  my $new_width        = int($width  / $factor + 0.5);
36
  my $new_height       = int($height / $factor + 0.5);
37
  my $thumbnail        = GD::Image->new($new_width, $new_height);
38

  
39
  $thumbnail->copyResized($image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
40

  
41
  $self->thumbnail_img_content($thumbnail->png);
42
  $self->thumbnail_img_content_type('image/png');
43
  $self->thumbnail_img_width($new_width);
44
  $self->thumbnail_img_height($new_height);
45
  return 1;
46

  
47
}
48

  
49
sub file_update_thumbnail {
50
  my ($self) = @_;
51

  
52
  return 1 if !$self->file_content || !$self->file_content_type || !Rose::DB::Object::Util::get_column_value_modified($self, 'file_content');
53
  $self->file_create_thumbnail;
54
  return 1;
55
}
56

  
57
sub file_probe_image_type {
58
  my ($self, $mime_type, $basefile) = @_;
59

  
60
  if ( !$supported_mime_types{ $mime_type } ) {
61
    $self->js->flash('error',t8('file \'#1\' has unsupported image type \'#2\' (supported types: #3)',
62
                                $basefile, $mime_type, join(' ', sort keys %supported_mime_types)));
63
    return 1;
64
  }
65
  return 0;
66
}
67

  
68
sub file_probe_type {
69
  my ($self) = @_;
70

  
71
  return (t8("No file uploaded yet")) if !$self->file_content;
72
  my $mime_type = File::MimeInfo::Magic::magic($self->file_content);
73

  
74
  my $info = Image::Info::image_info(\$self->{file_content});
75
  if (!$info || $info->{error} || !$info->{file_media_type} || !$supported_mime_types{ $info->{file_media_type} }) {
76
    $::lxdebug->warn("Image::Info error: " . $info->{error}) if $info && $info->{error};
77
    return (t8('Unsupported image type (supported types: #1)', join(' ', sort keys %supported_mime_types)));
78
  }
79

  
80
  $self->file_content_type($info->{file_media_type});
81
  $self->files_img_width($info->{width});
82
  $self->files_img_height($info->{height});
83
  $self->files_mtime(DateTime->now_local);
84

  
85
  $self->file_create_thumbnail;
86

  
87
  return ();
88
}
89

  
90
sub file_update_type_and_dimensions {
91
  my ($self) = @_;
92

  
93
  return () if !$self->file_content;
94
  return () if $self->file_content_type && $self->files_img_width && $self->files_img_height && !Rose::DB::Object::Util::get_column_value_modified($self, 'file_content');
95

  
96
  my @errors = $self->file_probe_type;
97
  return @errors if @errors;
98

  
99
  my $info = $supported_mime_types{ $self->file_content_type };
100
  if ($info->{convert_to_png}) {
101
    $self->file_content(GD::Image->new($self->file_content)->png);
102
    $self->file_content_type('image/png');
103
    $self->filename(apply { s/\.[^\.]+$//;  $_ .= '.png'; } $self->filename);
104
  }
105
  return ();
106
}
107

  
108
1;
109
__END__
110

  
111
=pod
112

  
113
=encoding utf8
114

  
115
=head1 NAME
116

  
117
  SL::DB::Helper::ThumbnailCreator - DatabaseClass Helper for Fileuploads
118

  
119
=head1 SYNOPSIS
120

  
121
  use SL::DB::Helper::ThumbnailCreator;
122

  
123
  # synopsis...
124

  
125
=head1 DESCRIPTION
126

  
127
  # longer description..
128

  
129
=head1 AUTHOR
130

  
131
  Werner Hahn E<lt>wh@futureworldsearch.netE<gt>
132

  
133
=cut
134

  
135

  
136
=head1 INTERFACE
137

  
138

  
139
=head1 DEPENDENCIES
140

  
141

  
142
=head1 SEE ALSO
143

  
144

  
SL/Form.pm
84 84
use List::Util qw(first max min sum);
85 85
use List::MoreUtils qw(all any apply);
86 86
use SL::DB::Tax;
87
use SL::Helper::File qw(:all);
88
use SL::Helper::CreatePDF qw(merge_pdfs);
87 89

  
88 90
use strict;
89 91

  
......
1076 1078
  # therefore copy to webdav, even if we do not have the webdav feature enabled (just archive)
1077 1079
  my $copy_to_webdav =  $::instance_conf->get_webdav_documents && !$self->{preview} && $self->{tmpdir} && $self->{tmpfile} && $self->{type};
1078 1080

  
1081
  if ( $ext_for_format eq 'pdf' && $::instance_conf->get_doc_storage ) {
1082
    $self->append_general_pdf_attachments(filepath =>  $self->{tmpdir}."/".$self->{tmpfile},
1083
                                          type     =>  $self->{type});
1084
  }
1079 1085
  if ($self->{media} eq 'file') {
1080 1086
    copy(join('/', $self->{cwd}, $userspath, $self->{tmpfile}), $out =~ m|^/| ? $out : join('/', $self->{cwd}, $out)) if $template->uses_temp_file;
1081 1087
    Common::copy_file_to_webdav_folder($self)                                                                         if $copy_to_webdav;
1088
    if (!$self->{preview} && $::instance_conf->get_doc_storage)
1089
    {
1090
      $self->{attachment_filename} ||= $self->generate_attachment_filename;
1091
      $self->store_pdf($self);
1092
    }
1082 1093
    $self->cleanup;
1083 1094
    chdir("$self->{cwd}");
1084 1095

  
......
1089 1100

  
1090 1101
  Common::copy_file_to_webdav_folder($self) if $copy_to_webdav;
1091 1102

  
1103
  if ( !$self->{preview} && $ext_for_format eq 'pdf' && $::instance_conf->get_doc_storage) {
1104
    $self->{attachment_filename} ||= $self->generate_attachment_filename;
1105
    $self->store_pdf($self);
1106
  }
1092 1107
  if ($self->{media} eq 'email') {
1093 1108

  
1094 1109
    my $mail = Mailer->new;
......
2453 2468
}
2454 2469

  
2455 2470
sub new_lastmtime {
2456
  $main::lxdebug->enter_sub();
2457 2471

  
2458 2472
  my ($self, $table, $provided_dbh) = @_;
2459 2473

  
......
2465 2479
  my $ref         = selectfirst_hashref_query($self, $dbh, $query, $self->{id});
2466 2480
  $ref->{mtime} ||= $ref->{itime};
2467 2481
  $self->{lastmtime} = $ref->{mtime};
2468
  $main::lxdebug->message(LXDebug->DEBUG2(),"new lastmtime=".$self->{lastmtime});
2469 2482

  
2470
  $main::lxdebug->leave_sub();
2471 2483
}
2472 2484

  
2473 2485
sub mtime_ischanged {
......
3000 3012

  
3001 3013
#--- 4 locale ---#
3002 3014
# $main::locale->text('SAVED')
3015
# $main::locale->text('SCREENED')
3003 3016
# $main::locale->text('DELETED')
3004 3017
# $main::locale->text('ADDED')
3005 3018
# $main::locale->text('PAYMENT POSTED')
......
3012 3025
# $main::locale->text('MAILED')
3013 3026
# $main::locale->text('SCREENED')
3014 3027
# $main::locale->text('CANCELED')
3028
# $main::locale->text('IMPORT')
3029
# $main::locale->text('UNIMPORT')
3015 3030
# $main::locale->text('invoice')
3016 3031
# $main::locale->text('proforma')
3017 3032
# $main::locale->text('sales_order')
SL/Helper/File.pm
1
package SL::Helper::File;
2

  
3
use strict;
4

  
5
use Exporter 'import';
6
our @EXPORT_OK = qw(store_pdf append_general_pdf_attachments);
7
our %EXPORT_TAGS = (all => \@EXPORT_OK,);
8
use SL::File;
9

  
10
sub store_pdf {
11
  my ($self, $form) = @_;
12
  return unless $::instance_conf->get_doc_storage;
13
  my $type = $form->{type};
14
  $type = $form->{formname} if $form->{formname} && !$form->{type};
15
  my $id = $form->{id};
16
  $id = $form->{attachment_id} if $form->{attachment_id} && !$form->{id};
17
  return if !$id || !$type;
18
  my $prefix = $form->get_number_prefix_for_type();
19
  SL::File->save(
20
    object_id   => $id,
21
    object_type => $type,
22
    mime_type   => 'application/pdf',
23
    source      => 'created',
24
    file_type   => 'document',
25
    file_name   => $form->{attachment_filename},
26
    file_path   => $form->{tmpfile},
27
    file_number => $form->{"${prefix}number"},
28
  );
29
}
30

  
31
# This method also needed by $form to append all general pdf attachments
32
#
33
sub append_general_pdf_attachments {
34
  my ($self, %params) = @_;
35
  return 0 unless $::instance_conf->get_doc_storage;
36
  return 0 if !$params{filepath} || !$params{type};
37

  
38
  my @files = SL::File->get_all(
39
    object_id   => 0,
40
    object_type => $params{type},
41
    mime_type   => 'application/pdf'
42
  );
43
  return 0 if $#files < 0;
44

  
45
  my @pdf_file_names = ($params{filepath});
46
  foreach my $file (@files) {
47
    my $path = $file->get_file;
48
    push @pdf_file_names, $path if $path;
49
  }
50

  
51
  #TODO immer noch das alte Problem:
52
  #je nachdem von woher der Aufruf kommt ist man in ./users oder .
53
  my $savedir = POSIX::getcwd();
54
  chdir("$self->{cwd}");
55
  $self->merge_pdfs(
56
    file_names => \@pdf_file_names,
57
    out_path   => $params{filepath}
58
  );
59
  chdir("$savedir");
60

  
61
  return 0;
62
}
63

  
64
1;
65

  
66
__END__
67

  
68
=encoding utf-8
69

  
70
=head1 NAME
71

  
72
SL::Helper::File - Helper for $::Form to store generated PDF-Documents
73

  
74

  
75
=head1 SYNOPSIS
76

  
77
# This Helper is used by SL::Form to store new generated PDF-Files and append general attachments to this documents.
78
#
79
# in SL::Form.pm:
80

  
81
 $self->store_pdf($self);
82

  
83
 $self->append_general_pdf_attachments($self) if ( $ext_for_format eq 'pdf' );
84

  
85
=head1 DESCRIPTION
86

  
87
The files with file_type "generated" are stored.
88

  
89
See also L<SL::File>.
90

  
91
=head1 METHODS
92

  
93

  
94
=head2 C<store_pdf>
95

  
96
Copy generated PDF-File to File destination.
97
This method is need from SL::Form after LaTeX-PDF Generation
98

  
99
=over 4
100

  
101
=item C<form.id>
102

  
103
ID of ERP-Document
104

  
105
=item C<form.type>
106

  
107
type of ERP-document
108

  
109
=item C<form.formname>
110

  
111
if no type is set this is used as type
112

  
113
=item C<form.attachment_id>
114

  
115
if no id is set this is used as id
116

  
117
=item C<form.tmpfile>
118

  
119
The path of the generated PDF-file
120

  
121
=item C<form.attachment_filename>
122

  
123
The generated filename which is used as new filename (without timestamp)
124

  
125
=back
126

  
127
=head2 C<append_general_pdf_attachments PARAMS>
128

  
129
This method also needed by SL::Form to append all general pdf attachments
130

  
131
needed C<PARAMS>:
132

  
133
=over 4
134

  
135
=item C<type>
136

  
137
type of ERP-document
138

  
139
=item C<outname>
140

  
141
Name of file to which the general attachments must be added
142

  
143
=back
144

  
145
=head1 AUTHOR
146

  
147
Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
148

  
149

  
150
=cut
151

  
js/kivi.File.js
1
namespace('kivi.File', function(ns) {
2

  
3
  ns.rename = function(id,type,file_type,checkbox_class,is_global) {
4
    var checkboxes = $('.'+checkbox_class).filter(function () { return  $(this).prop('checked'); });
5

  
6
    if (checkboxes.size() === 0) {
7
		  alert(kivi.t8("No file selected, please set one checkbox!"));
8
		  return false;
9
	  }
10
    if (checkboxes.size() > 1) {
11
	    alert(kivi.t8("More than one file selected, please set only one checkbox!"));
12
		  return false;
13
	  }
14
    var file_id = checkboxes[0].value;
15
    $('#newfilename_id').val($('#filename_'+file_id).text());
16
    $('#next_ids_id').val('');
17
    $('#is_global_id').val(is_global);
18
    $('#rename_id_id').val(file_id);
19
    $('#sessionfile_id').val('');
20
  	$('#rename_extra_text').html('');
21
    kivi.popup_dialog({
22
                      id:     'rename_dialog',
23
                      dialog: { title: kivi.t8("Rename attachment")
24
                               , width:  400
25
                               , height: 200
26
                               , modal:  true } });
27
    return true;
28
  }
29
  
30
  ns.renameclose = function() {
31
    $("#rename_dialog").dialog('close');
32
    return false;
33
  }
34
  
35
  ns.renameaction = function() {
36
	  $("#rename_dialog").dialog('close');
37
    var data = {
38
      action:          'File/ajax_rename',
39
      id:              $('#rename_id_id').val(),
40
	    to:              $('#newfilename_id').val(),
41
      next_ids:        $('#next_ids_id').val(),
42
      is_global:       $('#is_global_id').val(),
43
      sessionfile:     $('#sessionfile_id').val(),
44
    };
45
    $.post("controller.pl", data, kivi.eval_json_result);
46
    return true;
47
  }
48
  
49
  ns.askForRename = function(file_id,file_name,sessionfile,next_ids,is_global) {
50
    $('#newfilename_id').val(file_name);
51
    $('#rename_id_id').val(file_id);
52
    $('#is_global_id').val(is_global);
53
    $('#next_ids_id').val(next_ids);
54
    $('#sessionfile_id').val(sessionfile);
55
    $('#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"));
56
    kivi.popup_dialog(
57
      {
58
        id:     'rename_dialog',
59
        dialog: { title: kivi.t8("Rename attachment")
60
                  , width:  400
61
                  , height: 200
62
                  , modal:  true }
63
      });
64
  }
65

  
66
  ns.upload = function(id,type,filetype,upload_title,gl) {
67
    kivi.popup_dialog({ url:     'controller.pl',
68
                        data:    { action: 'File/ajax_upload',
69
                                   file_type:   filetype,
70
                                   object_type: type,
71
                                   object_id:   id,
72
                                   is_global:   gl
73
                                 },
74
                        id:     'files_upload',
75
                        dialog: { title: upload_title, width: 650, height: 240 } });
76
    return true;
77
  }
78

  
79
  ns.reset_upload_form = function() {
80
      $('#attachment_updfile').val('');
81
      $("#upload_result").html('');
82
      ns.allow_upload_submit();
83
  }
84

  
85
  ns.allow_upload_submit = function() {
86
      $('#upload_selected_button').prop('disabled',$('#upload_files').val() === '');
87
  }
88

  
89
  ns.upload_selected_files = function(id,type,filetype,maxsize,is_global) {
90
      var myform = document.getElementById("upload_form");
91
      var filesize  = 0;
92
      var myfiles = document.getElementById("upload_files").files;
93
      for ( i=0; i < myfiles.length; i++ ) {
94
          var fname ='';
95
          try {
96
              filesize  += myfiles[i].size;
97
              fname = encodeURIComponent(myfiles[i].name);
98
          }
99
          catch(err) {
100
              fname ='';
101
              try {
102
                  fname = myfiles[i].name;
103
              }
104
              catch(err2) { fname ='';}
105
              $("#upload_result").html(kivi.t8("filename has not uploadable characters ")+fname);
106
              return;	  
107
          }
108
      }
109
      if ( filesize > maxsize ) {
110
          $("#upload_result").html(kivi.t8("filesize too big: ")+
111
                                   filesize+ kivi.t8(" bytes, max=") + maxsize );
112
          return;
113
      }
114

  
115
      myform.action ="controller.pl?action=File/ajax_files_uploaded&json=1&object_type="+
116
          type+'&object_id='+id+'&file_type='+filetype+'&is_global='+is_global;
117
      var oReq = new XMLHttpRequest();
118
      oReq.onload            = ns.attSuccess;
119
      oReq.upload.onprogress = ns.attProgress;
120
      oReq.upload.onerror    = ns.attFailed;
121
      oReq.upload.onabort    = ns.attCanceled;
122
      oReq.open("post",myform.action, true);
123
      $("#upload_result").html(kivi.t8("start upload"));
124
      oReq.send(new FormData(myform));
125
  }
126

  
127
  ns.attProgress = function(oEvent) {
128
      if (oEvent.lengthComputable) {
129
          var percentComplete = (oEvent.loaded / oEvent.total) * 100;
130
          $("#upload_result").html(percentComplete+" % "+ kivi.t8("uploaded"));
131
      }
132
  }
133

  
134
  ns.attFailed = function(evt) {
135
      $('#files_upload').dialog('close');
136
      $("#upload_result").html(kivi.t8("An error occurred while transferring the file."));
137
  }
138

  
139
  ns.attCanceled = function(evt) {
140
      $('#files_upload').dialog('close');
141
      $("#upload_result").html(kivi.t8("The transfer has been canceled by the user."));
142
  }
143

  
144
  ns.attSuccess = function() {
145
      $('#files_upload').dialog('close');
146
      kivi.eval_json_result(jQuery.parseJSON(this.response));
147
  }
148

  
149
  ns.delete = function(id,type,file_type,checkbox_class,is_global) {
150
    var checkboxes = $('.'+checkbox_class).filter(function () { return  $(this).prop('checked'); });
151

  
152
    if ((checkboxes.size() === 0) || 
153
        !confirm(kivi.t8('Do you really want to delete the selected documents?')))
154
      return false;
155
    var data = {
156
      action     :  'File/ajax_delete',
157
      object_id  :  id,
158
      object_type:  type,
159
      file_type  :  file_type,
160
      ids        :  checkbox_class,
161
      is_global  :  is_global,
162
    };
163
    $.post("controller.pl?" + checkboxes.serialize(), data, kivi.eval_json_result);
164
    return false;
165
  }
166

  
167
  ns.unimport = function(id,type,file_type,checkbox_class) {
168
    var checkboxes = $('.'+checkbox_class).filter(function () { return  $(this).prop('checked'); });
169

  
170
    if ((checkboxes.size() === 0) || 
171
        !confirm(kivi.t8('Do you really want to unimport the selected documents?')))
172
      return false;
173
    var data = {
174
      action     :  'File/ajax_unimport',
175
      object_id  :  id,
176
      object_type:  type,
177
      file_type  :  file_type,
178
      ids        :  checkbox_class,
179
    };
180
    $.post("controller.pl?" + checkboxes.serialize(), data, kivi.eval_json_result);
181
    return false;
182
  }
183

  
184
  ns.update = function(id,type,file_type,is_global) {
185
    var data = {
186
      action:       'File/list',
187
      json:         1,
188
      object_type:  type,
189
      object_id:    id,
190
      file_type:    file_type,
191
      is_global:    is_global
192
    };
193

  
194
    $.post("controller.pl", data, kivi.eval_json_result);
195
    return false;
196
  }
197

  
198
  ns.import = function (id,type,file_type,fromwhere,frompath) {
199
    kivi.popup_dialog({ url:     'controller.pl',
200
                        data:    { action      : 'File/ajax_importdialog',
201
                                   object_type : type,
202
                                   source      : fromwhere,
203
                                   path        : frompath,
204
                                   file_type   : file_type,
205
                                   object_id   : id
206
                                 },
207
                        id:     'import_dialog',
208
                        dialog: { title: kivi.t8('Import documents from #1',[fromwhere]), width: 420, height: 540 }
209
                      });
210
    return true;
211
  }
212
    
213
  ns.importclose = function() {
214
    $("#import_dialog").dialog('close');
215
    return false;
216
  }
217
  
218
  ns.importaction = function(id,type,file_type,fromwhere,frompath,checkbox_class) {
219
    var checkboxes = $('.'+checkbox_class).filter(function () { return  $(this).prop('checked'); });
220

  
221
	  $("#import_dialog").dialog('close');
222
    if (checkboxes.size() === 0) {
223
		  return false;
224
	  }
225
  	var data = {
226
        action     : 'File/ajax_import',
227
        object_id  : id,
228
        object_type: type,
229
        file_type  : file_type,
230
        source     : fromwhere,
231
        path       : frompath,
232
        ids        : checkbox_class
233
    };
234
    $.post("controller.pl?" + checkboxes.serialize(), data, kivi.eval_json_result);
235
    return true;
236
  }
237

  
238

  
239
  ns.init = function() {
240
  }
241

  
242
});
locale/de/all
1475 1475
  'Illegal date'                => 'Ungültiges Datum',
1476 1476
  'Image'                       => 'Grafik',
1477 1477
  'Import'                      => 'Import',
1478
  'Import AP from Scanner or Email' => 'Einkaufsbelege importieren vom Scanner oder von Email',
1479
  'Import AR from Scanner or Email' => 'Verkaufsbelege importieren vom Scanner oder von Email',
1478 1480
  'Import CSV'                  => 'CSV-Import',
1479 1481
  'Import Status'               => 'Import Status',
1480 1482
  'Import a MT940 file:'        => 'Laden Sie eine MT940 Datei hoch:',
sql/Pg-upgrade2-auth/other_file_sources.sql
1
-- @tag: other_file_sources
2
-- @description: Neue Gruppenrechte für das Importieren von Scannern oder email
3
-- @depends: release_3_4_0 master_rights_position_gaps
4
-- @locales: Import AP from Scanner or Email
5
-- @locales: Import AR from Scanner or Email
6
INSERT INTO auth.master_rights (position, name, description) VALUES (2050, 'import_ar', 'Import AR from Scanner or Email');
7
INSERT INTO auth.master_rights (position, name, description) VALUES (2650, 'import_ap', 'Import AP from Scanner or Email');
templates/webpages/file/import_dialog.html
1
[%- USE L -%][%- USE LxERP -%][%- USE JavaScript -%]
2

  
3
<form method="post" id="file_import_form" action="controller.pl">
4
 <table>
5
  <thead>
6
   <tr>
7
    <th class="listheading" width="3%">[% L.checkbox_tag(source.chk_action _ '_checkall') %]</th>
8
    <th class="listheading" width="11%">[% source.chkall_title %]</th>
9
     <th>[% LxERP.t8("Attached Filename") %]</th>
10
   </tr>
11
  </thead>
12
  <tbody>
13
   [%- FOREACH file = source.files %]
14
   <tr class="listrow[% loop.count % 2 %]">
15
   <td>[%- L.checkbox_tag(source.chk_action _ '[]', 'value'=file.name, 'class'=source.chk_action) %]</td>
16
   <td></td>
17
   <td><span id="[% "filename_" _ file.name %]">[% file.filename %]</span></td>
18
   </tr>
19
   [%- END %]
20
  </tbody>
21
 </table>
22

  
23
 <p>
24
   [%- L.button_tag("kivi.File.importaction(" _ SELF.object_id _ ",'" _ SELF.object_type _ "','" _ SELF.file_type _ "','" _ source.name _ "','" _ source.path _ "','"_ source.chk_action _ "');", LxERP.t8('Continue'), id => "import_cont_btn") %]</td>
25
   [%- L.button_tag("kivi.File.importclose();" , LxERP.t8('Cancel')  , class => "submit") %]</td></tr>
26
 </p>
27

  
28
</form>
29

  
30
<script type="text/javascript">
31
<!--
32
$(function() {
33
  $('#[% source.chk_action %]_checkall').checkall('INPUT[name="[% source.chk_action %][]"]');
34
});
35
-->
36
</script>
templates/webpages/file/list.html
1
[%- USE LxERP -%][% USE L %][% USE HTML %]
2
[%- IF ! json %]
3
<div id="[% file_type %]_list_[% object_type %]">
4
[%- END %]
5
<div class="listtop">[% title %]</div>
6

  
7
<div style="padding-bottom: 15px">
8
[%- SET can_rename = 0 %]
9
[%- FOREACH source = SOURCES %]
10
 <table style="width: 100%" >
... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet.

Auch abrufbar als: Unified diff