5 use parent qw(Rose::Object);
 
  12 use SL::Helper::UserPreferences;
 
  15 use constant RENAME_OK          => 0;
 
  16 use constant RENAME_EXISTS      => 1;
 
  17 use constant RENAME_NOFILE      => 2;
 
  18 use constant RENAME_SAME        => 3;
 
  19 use constant RENAME_NEW_VERSION => 4;
 
  22   my ($self, %params) = @_;
 
  23   die 'no id' unless $params{id};
 
  24   my $dbfile = SL::DB::Manager::File->get_first(query => [id => $params{id}]);
 
  25   die 'not found' unless $dbfile;
 
  26   $main::lxdebug->message(LXDebug->DEBUG2(), "object_id=".$dbfile->object_id." object_type=".$dbfile->object_type." dbfile=".$dbfile);
 
  27   SL::File::Object->new(db_file => $dbfile, id => $dbfile->id, loaded => 1);
 
  30 sub get_version_count {
 
  31   my ($self, %params) = @_;
 
  32   die "no id or dbfile" unless $params{id} || $params{dbfile};
 
  33   $params{dbfile} = SL::DB::Manager::File->get_first(query => [id => $params{id}]) if !$params{dbfile};
 
  34   die 'not found' unless $params{dbfile};
 
  35   my $backend = $self->_get_backend($params{dbfile}->backend);
 
  36   return $backend->get_version_count(%params);
 
  40   my ($self, %params) = @_;
 
  43   return @files unless $params{object_type};
 
  44   return @files unless defined($params{object_id});
 
  47     object_id   => $params{object_id},
 
  48     object_type => $params{object_type}
 
  50   push @query, (file_name => $params{file_name}) if $params{file_name};
 
  51   push @query, (file_type => $params{file_type}) if $params{file_type};
 
  52   push @query, (mime_type => $params{mime_type}) if $params{mime_type};
 
  53   push @query, (source    => $params{source})    if $params{source};
 
  55   my $sortby = $params{sort_by} || 'itime DESC,file_name ASC';
 
  57   @files = @{ SL::DB::Manager::File->get_all(query => [@query], sort_by => $sortby) };
 
  58   map { SL::File::Object->new(db_file => $_, id => $_->id, loaded => 1) } @files;
 
  61 sub get_all_versions {
 
  62   my ($self, %params) = @_;
 
  64   my @fileobjs = $self->get_all(%params);
 
  65   if ( $params{dbfile} ) {
 
  66     push @fileobjs, SL::File::Object->new(dbfile => $params{db_file}, id => $params{dbfile}->id, loaded => 1);
 
  68     @fileobjs = $self->get_all(%params);
 
  70   foreach my $fileobj (@fileobjs) {
 
  71     $main::lxdebug->message(LXDebug->DEBUG2(), "obj=" . $fileobj . " id=" . $fileobj->id." versions=".$fileobj->version_count);
 
  72     my $maxversion = $fileobj->version_count;
 
  73     push @versionobjs, $fileobj;
 
  74     if ($maxversion > 1) {
 
  75       for my $version (2..$maxversion) {
 
  76         $main::lxdebug->message(LXDebug->DEBUG2(), "clone for version=".($maxversion-$version+1));
 
  78           my $clone = clone($fileobj);
 
  79           $clone->version($maxversion-$version+1);
 
  81           $main::lxdebug->message(LXDebug->DEBUG2(), "clone version=".$clone->version." mtime=". $clone->mtime);
 
  82           push @versionobjs, $clone;
 
  92   my ($self, %params) = @_;
 
  93   return 0 unless $params{object_type};
 
  96     object_id   => $params{object_id},
 
  97     object_type => $params{object_type}
 
  99   push @query, (file_name => $params{file_name}) if $params{file_name};
 
 100   push @query, (file_type => $params{file_type}) if $params{file_type};
 
 101   push @query, (mime_type => $params{mime_type}) if $params{mime_type};
 
 102   push @query, (source    => $params{source})    if $params{source};
 
 104   my $cnt = SL::DB::Manager::File->get_all_count(query => [@query]);
 
 109   my ($self, %params) = @_;
 
 110   return 0 unless defined($params{object_id}) || $params{object_type};
 
 111   my $files = SL::DB::Manager::File->get_all(
 
 113       object_id   => $params{object_id},
 
 114       object_type => $params{object_type}
 
 117   foreach my $file (@{$files}) {
 
 118     $params{dbfile} = $file;
 
 119     $self->delete(%params);
 
 124   my ($self, %params) = @_;
 
 125   die "no id or dbfile" unless $params{id} || $params{dbfile};
 
 128     $rc = SL::DB->client->with_transaction(\&_delete, $self, %params);
 
 135   my ($self, %params) = @_;
 
 136   $params{dbfile} = SL::DB::Manager::File->get_first(query => [id => $params{id}]) if !$params{dbfile};
 
 138   my $backend = $self->_get_backend($params{dbfile}->backend);
 
 139   if ( $params{dbfile}->file_type eq 'document' && $params{dbfile}->source ne 'created')
 
 142     my $hist = SL::DB::Manager::History->get_first(
 
 144         addition  => 'IMPORT',
 
 145         trans_id  => $params{dbfile}->object_id,
 
 146         what_done => $params{dbfile}->id
 
 151       if (!$main::auth->assert('import_ar | import_ap', 1)) {
 
 152         die 'no permission to unimport';
 
 154       my $file = $backend->get_filepath(dbfile => $params{dbfile});
 
 155       $main::lxdebug->message(LXDebug->DEBUG2(), "del file=" . $file . " to=" . $hist->snumbers);
 
 156       File::Copy::copy($file, $hist->snumbers) if $file;
 
 157       $hist->addition('UNIMPORT');
 
 161   if ($backend->delete(%params)) {
 
 163     if ( $params{last} || $params{all_but_notlast} ) {
 
 164       if ( $backend->get_version_count > 0 ) {
 
 165         $params{dbfile}->mtime(DateTime->now_local);
 
 166         $params{dbfile}->save;
 
 173     $params{dbfile}->delete if $do_delete;
 
 180   my ($self, %params) = @_;
 
 184     $obj = SL::DB->client->with_transaction(\&_save, $self, %params);
 
 191   my ($self, %params) = @_;
 
 192   my $file = $params{dbfile};
 
 196     $file = SL::DB::File->new(id => $params{id})->load;
 
 197     die 'dbfile not exists'     unless $file;
 
 199   $main::lxdebug->message(LXDebug->DEBUG2(), "obj_id=" .$params{object_id});
 
 200     die 'no object type set'    unless $params{object_type};
 
 201     die 'no object id set'      unless defined($params{object_id});
 
 203     $exists = $self->get_all_count(%params);
 
 204     die 'filename still exist' if $exists && $params{fail_if_exists};
 
 206       my ($obj1) = $self->get_all(%params);
 
 207       $file = $obj1->db_file;
 
 209       $file = SL::DB::File->new();
 
 210       $file->assign_attributes(
 
 211         object_id      => $params{object_id},
 
 212         object_type    => $params{object_type},
 
 213         source         => $params{source},
 
 214         file_type      => $params{file_type},
 
 215         file_name      => $params{file_name},
 
 216         mime_type      => $params{mime_type},
 
 217         title          => $params{title},
 
 218         description    => $params{description},
 
 225     #change attr on existing file
 
 226     $file->file_name  ($params{file_name})   if $params{file_name};
 
 227     $file->mime_type  ($params{mime_type})   if $params{mime_type};
 
 228     $file->title      ($params{title})       if $params{title};
 
 229     $file->description($params{description}) if $params{description};
 
 231   if ( !$file->backend ) {
 
 232     $file->backend($self->_get_backend_by_file_type($file));
 
 233     # load itime for new file
 
 236   $main::lxdebug->message(LXDebug->DEBUG2(), "backend3=" .$file->backend);
 
 237   my $backend = $self->_get_backend($file->backend);
 
 238   $params{dbfile} = $file;
 
 239   $backend->save(%params);
 
 241   $file->mtime(DateTime->now_local);
 
 243   if ($params{file_type} eq 'document' && $params{source} ne 'created') {
 
 244     SL::DB::History->new(
 
 245       addition    => 'IMPORT',
 
 246       trans_id    => $params{object_id},
 
 247       snumbers    => $params{file_path},
 
 248       employee_id => SL::DB::Manager::Employee->current->id,
 
 249       what_done   => $params{dbfile}->id
 
 252   return $params{obj} if $params{dbfile} && $params{obj};
 
 253   return SL::File::Object->new(db_file => $file, id => $file->id, loaded => 1);
 
 257   my ($self, %params) = @_;
 
 258   return RENAME_NOFILE unless $params{id} || $params{dbfile};
 
 259   my $file = $params{dbfile};
 
 260   $file = SL::DB::Manager::File->get_first(query => [id => $params{id}]) if !$file;
 
 261   return RENAME_NOFILE unless $file;
 
 263   $main::lxdebug->message(LXDebug->DEBUG2(), "rename id=" . $file->id . " to=" . $params{to});
 
 265     return RENAME_SAME   if $params{to} eq $file->file_name;
 
 266     return RENAME_EXISTS if $self->get_all_count( object_id     => $file->object_id,
 
 267                                                   object_type   => $file->object_type,
 
 268                                                   mime_type     => $file->mime_type,
 
 269                                                   source        => $file->source,
 
 270                                                   file_type     => $file->file_type,
 
 271                                                   file_name     => $params{to}
 
 274     my $backend = $self->_get_backend($file->backend);
 
 275     $backend->rename(dbfile => $file) if $backend;
 
 276     $file->file_name($params{to});
 
 282 sub get_backend_class {
 
 283   my ($self, $backendname) = @_;
 
 284   die "no backend name set" unless $backendname;
 
 285   $self->_get_backend($backendname);
 
 288 sub get_other_sources {
 
 290   my $pref = SL::Helper::UserPreferences->new(namespace => 'file_sources');
 
 291   $pref->login("#default#");
 
 293   foreach my $tuple (@{ $pref->get_all() }) {
 
 294     my %lkeys  = %{ SL::JSON::from_json($tuple->{value}) };
 
 296       'name'        => $tuple->{key},
 
 297       'description' => $lkeys{desc},
 
 298       'directory'   => $lkeys{dir}
 
 300     push @sources, $source;
 
 305 sub sync_from_backend {
 
 306   my ($self, %params) = @_;
 
 307   return unless $params{file_type};
 
 308   my $file = SL::DB::File->new;
 
 309   $file->file_type($params{file_type});
 
 310   my $backend = $self->_get_backend(dbfile => $file->backend);
 
 311   return unless $backend;
 
 312   $backend->sync_from_backend(%params);
 
 319   my ($self, $backend_name) = @_;
 
 320   my $class = 'SL::File::Backend::' . $backend_name;
 
 322   die $::locale->text('no backend enabled') if $backend_name eq 'None';
 
 324     eval "require $class";
 
 326     die $::locale->text('backend "#1" not enabled',$backend_name) unless $obj->enabled;
 
 329     die $::locale->text('backend "#1" not found',$backend_name);
 
 334 sub _get_backend_by_file_type {
 
 335   my ($self, $dbfile) = @_;
 
 337   $main::lxdebug->message(LXDebug->DEBUG2(), "_get_backend_by_file_type=" .$dbfile." type=".$dbfile->file_type);
 
 338   return "Filesystem" unless $dbfile;
 
 339   return $::instance_conf->get_doc_storage_for_documents   if $dbfile->file_type eq 'document';
 
 340   return $::instance_conf->get_doc_storage_for_attachments if $dbfile->file_type eq 'attachment';
 
 341   return $::instance_conf->get_doc_storage_for_images      if $dbfile->file_type eq 'image';
 
 355 SL::File - The intermediate Layer for handling files
 
 359   # In a controller or helper ( see SL::Controller::File or SL::Helper::File )
 
 360   # you can create, remove, delete etc. a file in a backend independent way
 
 362   my $file  = SL::File->save(
 
 363                      object_id     => $self->object_id,
 
 364                      object_type   => $self->object_type,
 
 365                      mime_type     => 'application/pdf',
 
 366                      file_type     => 'documents',
 
 367                      file_contents => 'this is no pdf');
 
 369   my $file1  = SL::File->get(id => $id);
 
 370   SL::File->delete(id => $id);
 
 371   SL::File->delete(dbfile => $file1);
 
 372   SL::File->delete_all(object_id   => $object_id,
 
 373                        object_type => $object_type,
 
 374                        file_type   => $filetype      # may be optional
 
 376   SL::File->rename(id => $id,to => $newname);
 
 377   my $files1 = SL::File->get_all(object_id   => $object_id,
 
 378                                  object_type => $object_type,
 
 379                                  file_type   => 'images',  # may be optional
 
 380                                  source      => 'uploaded' # may be optional
 
 383   # Alternativelly some operation can be done with the filemangement object wrapper
 
 384   # and additional oparations see L<SL::File::Object>
 
 388 The Filemanagemt can handle files in a storage independent way. Internal the File
 
 389 use the configured storage backend for the type of file.
 
 390 These backends must be configured in L<SL::Controller::ClientConfig> or an extra database table.
 
 392 There are three types of files:
 
 398 which can be generated files (for sales), scanned files or uploaded files (for purchase) for an ERP-object.
 
 399 They can exist in different versions. The versioning is handled implicit. All versions of a file may be
 
 400 deleted by the user if she/he is allowed to do this.
 
 404 which have additional information for an ERP-objects. They are uploadable. If a filename still exists
 
 405 on a ERP-Object the new uploaded file is a new version of this or it must be renamed by user.
 
 407 There are generic attachments for a specific document group (like sales_invoices). This attachments can be
 
 408 combinide/merged with the document-file in the time of printing.
 
 409 Today only PDF-Attachmnets can be merged with the generated document-PDF.
 
 413 they are like attachments, but they may be have thumbnails for displaying.
 
 414 So the must have an image format like png,jpg. The versioning is like attachments
 
 418 For each type of files the backend can configured in L<SL::Controller::ClientConfig>.
 
 420 The files have also the parameter C<Source>:
 
 424 =item - created, generated by LaTeX
 
 428 =item - scanner, import from scanner
 
 430 ( or scanner1, scanner2 if there are different scanner, be configurable via UserPreferences )
 
 432 =item - email, received by email and imported by hand or automatic.
 
 436 The files from source 'scanner' or 'email' are not allowed to delete else they must be send back to the sources.
 
 437 This means they are moved back into the correspondent source directories.
 
 439 The scanner and email import must be configured  via Table UserPreferences:
 
 443  id |  login  |  namespace   | version |   key    |                        value
 
 444 ----+---------+--------------+---------+----------+------------------------------------------------------
 
 445   1 | default | file_sources | 0.00000 | scanner1 | {"dir":"/var/tmp/scanner1","desc":"Scanner Einkauf" }
 
 446   2 | default | file_sources | 0.00000 | emails   | {"dir":"/var/tmp/emails"  ,"desc":"Empfangene Mails"}
 
 452 The Fileinformation is stored in the table L<SL::DB::File> for saving the information.
 
 453 The modul and object_id describe the link to the object.
 
 455 The interface SL::File:Object encapsulate SL::DB:File, see L<SL::DB::Object>
 
 457 The storage backends are extra classes which depends from L<SL::File::Backend>.
 
 458 So additional backend classes can be added.
 
 460 The implementation of versioning is done in the different backends.
 
 468 Creates a new SL::DB:File object or save an existing object for a specific backend depends of the C<file_type>
 
 474                          object_id    => $self->object_id,
 
 475                          object_type  => $self->object_type,
 
 476                          content_type => 'application/pdf'
 
 483 The file data is stored in the backend. If the file_type is "document" and the source is not "created" the file is imported,
 
 484 so in the history the import is documented also as a hint to can unimport the file later.
 
 492 The id of SL::DB::File for an existing file
 
 496 The Id of the ERP-object for a new file.
 
 500 The Type of the ERP-object like "sales_quotation" for a new file. A clear mapping to the class/model exists in the controller.
 
 504 The type may be "documents", "attachments" or "images" for a new file.
 
 508 The type may be "created", "uploaded" or email sources or scanner sources for a new file.
 
 512 The file_name of the file for a new file. This name is used in the WebGUI and as name for download.
 
 516 The mime_type of a new file. This is used for downloading or for email attachments.
 
 518 =item C<description> or C<title>
 
 520 The description or title of a new file. This must be discussed if this attribute is needed.
 
 524 =item C<delete PARAMS>
 
 526 The file data is deleted in the backend. If the file comes from source 'scanner' or 'email'
 
 527 they moved back to the source folders. This is documented in the history.
 
 535 The id of SL::DB::File
 
 539 As alternative if the SL::DB::File as object is available.
 
 543 =item C<delete_all PARAMS>
 
 545 All file data of an ERP-object is deleted in the backend.
 
 551 The Id of the ERP-object.
 
 555 The Type of the ERP-object like "sales_quotation". A clear mapping to the class/model exists in the controller.
 
 559 =item C<rename PARAMS>
 
 561 The Filename of the file is changed
 
 569 The id of SL::DB::File
 
 579 The actual file object is retrieved. The id of the object is needed.
 
 587 The id of SL::DB::File
 
 591 =item C<get_all PARAMS>
 
 593 All last versions of file data objects which are related to an ERP-Document,Part,Customer,Vendor,... are retrieved.
 
 601 The Id of the ERP-object.
 
 605 The Type of the ERP-object like "sales_quotation". A clear mapping to the class/model exists in the controller.
 
 609 The type may be "documents", "attachments" or "images". This parameter is optional.
 
 613 The name of the file . This parameter is optional.
 
 617 The MIME type of the file . This parameter is optional.
 
 621 The type may be "created", "uploaded" or email sources or scanner soureces. This parameter is optional.
 
 625 An optional parameter in which sorting the files are retrieved. Default is decrementing itime and ascending filename
 
 629 =item C<get_all_versions PARAMS>
 
 631 All versions of file data objects which are related to an ERP-Document,Part,Customer,Vendor,... are retrieved.
 
 632 If only the versions of one file are wanted, additional parameter like file_name must be set.
 
 633 If the param C<dbfile> set, only the versions of this file are returned.
 
 635 Available C<PARAMS> ar the same as L<get_all>
 
 639 =item C<get_all_count PARAMS>
 
 641 The count of available files is returned.
 
 642 Available C<PARAMS> ar the same as L<get_all>
 
 645 =item C<get_content PARAMS>
 
 647 The data of a file can retrieved. A reference to the data is returned.
 
 655 The id of SL::DB::File
 
 659 If no Id exists the object SL::DB::File as param.
 
 663 =item C<get_file_path PARAMS>
 
 665 Sometimes it is more useful to have a path to the file not the contents. If the backend has not stored the content as file
 
 666 it is in the responsibility of the backend to create a tempory session file.
 
 674 The id of SL::DB::File
 
 678 If no Id exists the object SL::DB::File as param.
 
 682 =item C<get_other_sources>
 
 684 A helpful method to get the sources for scanner and email from UserPreferences. This method is need from SL::Controller::File
 
 686 =item C<sync_from_backend>
 
 688 For Backends which may be changed outside of kivitendo a synchronization of the database is done.
 
 689 This sync must be triggered by a periodical task.
 
 697 The synchronization is done file_type by file_type.
 
 705 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>