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};
126 my $rc = SL::DB->client->with_transaction(\&_delete, $self, %params);
128 my $err = SL::DB->client->error;
129 die (ref $err?$$err:$err);
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) = @_;
182 my $obj = SL::DB->client->with_transaction(\&_save, $self, %params);
184 my $err = SL::DB->client->error;
185 die (ref $err?$$err:$err);
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;
323 eval "require $class";
325 die \'backend not enabled' unless $obj->enabled;
328 die \'backend class not found';
333 sub _get_backend_by_file_type {
334 my ($self, $dbfile) = @_;
336 $main::lxdebug->message(LXDebug->DEBUG2(), "_get_backend_by_file_type=" .$dbfile." type=".$dbfile->file_type);
337 return "Filesystem" unless $dbfile;
338 return $::instance_conf->get_doc_storage_for_documents if $dbfile->file_type eq 'document';
339 return $::instance_conf->get_doc_storage_for_attachments if $dbfile->file_type eq 'attachment';
340 return $::instance_conf->get_doc_storage_for_images if $dbfile->file_type eq 'image';
354 SL::File - The intermediate Layer for handling files
358 # In a controller or helper ( see SL::Controller::File or SL::Helper::File )
359 # you can create, remove, delete etc. a file in a backend independant way
361 my $file = SL::File->save(
362 object_id => $self->object_id,
363 object_type => $self->object_type,
364 mime_type => 'application/pdf',
365 file_type => 'documents',
366 file_contents => 'this is no pdf');
368 my $file1 = SL::File->get(id => $id);
369 SL::File->delete(id => $id);
370 SL::File->delete(dbfile => $file1);
371 SL::File->delete_all(object_id => $object_id,
372 object_type => $object_type,
373 file_type => $filetype # may be optional
375 SL::File->rename(id => $id,to => $newname);
376 my $files1 = SL::File->get_all(object_id => $object_id,
377 object_type => $object_type,
378 file_type => 'images', # may be optional
379 source => 'uploaded' # may be optional
382 # Alternativelly some operation can be done with the filemangement object wrapper
383 # and additional oparations see L<SL::File::Object>
387 The Filemanagemt can handle files in a storage independant way. Internal the File
388 use the configured storage backend for the type of file.
389 These backends must be configured in L<SL::Controller::ClientConfig> or an extra database table.
391 There are three types of files:
397 which can be generated files (for sales), scanned files or uploaded files (for purchase) for an ERP-object.
398 They can exist in different versions. The versioning is handled implicit. All versions of a file may be
399 deleted by the user if she/he is allowed to do this.
403 which have additional information for an ERP-objects. They are uploadable. If a filename still exists
404 on a ERP-Object the new uploaded file is a new version of this or it must be renamed by user.
406 There are generic attachments for a specific document group (like sales_invoices). This attachments can be
407 combinide/merged with the document-file in the time of printing.
408 Today only PDF-Attachmnets can be merged with the generated document-PDF.
412 they are like attachments, but they may be have thumbnails for displaying.
413 So the must have an image format like png,jpg. The versioning is like attachments
417 For each type of files the backend can configured in L<SL::Controller::ClientConfig>.
419 The files have also the parameter C<Source>:
423 =item - created, generated by LaTeX
427 =item - scanner, import from scanner
429 ( or scanner1, scanner2 if there are different scanner, be configurable via UserPreferences )
431 =item - email, received by email and imported by hand or automatic.
435 The files from source 'scanner' or 'email' are not allowed to delete else they must be send back to the sources.
436 This means they are moved back into the correspondent source directories.
438 The scanner and email import must be configured via Table UserPreferences:
442 id | login | namespace | version | key | value
443 ----+---------+--------------+---------+----------+------------------------------------------------------
444 1 | default | file_sources | 0.00000 | scanner1 | {"dir":"/var/tmp/scanner1","desc":"Scanner Einkauf" }
445 2 | default | file_sources | 0.00000 | emails | {"dir":"/var/tmp/emails" ,"desc":"Empfangene Mails"}
451 The Fileinformation is stored in the table L<SL::DB::File> for saving the information.
452 The modul and object_id describe the link to the object.
454 The interface SL::File:Object encapsulate SL::DB:File, see L<SL::DB::Object>
456 The storage backends are extra classes which depends from L<SL::File::Backend>.
457 So additional backend classes can be added.
459 The implementation of versioning is done in the different backends.
467 Creates a new SL::DB:File object or save an existing object for a specific backend depends of the C<file_type>
473 object_id => $self->object_id,
474 object_type => $self->object_type,
475 content_type => 'application/pdf'
482 The file data is stored in the backend. If the file_type is "document" and the source is not "created" the file is imported,
483 so in the history the import is documented also as a hint to can unimport the file later.
491 The id of SL::DB::File for an existing file
495 The Id of the ERP-object for a new file.
499 The Type of the ERP-object like "sales_quotation" for a new file. A clear mapping to the class/model exists in the controller.
503 The type may be "documents", "attachments" or "images" for a new file.
507 The type may be "created", "uploaded" or email sources or scanner sources for a new file.
511 The file_name of the file for a new file. This name is used in the WebGUI and as name for download.
515 The mime_type of a new file. This is used for downloading or for email attachments.
517 =item C<description> or C<title>
519 The description or title of a new file. This must be discussed if this attribute is needed.
523 =item C<delete PARAMS>
525 The file data is deleted in the backend. If the file comes from source 'scanner' or 'email'
526 they moved back to the source folders. This is documented in the history.
534 The id of SL::DB::File
538 As alternative if the SL::DB::File as object is available.
542 =item C<delete_all PARAMS>
544 All file data of an ERP-object is deleted in the backend.
550 The Id of the ERP-object.
554 The Type of the ERP-object like "sales_quotation". A clear mapping to the class/model exists in the controller.
558 =item C<rename PARAMS>
560 The Filename of the file is changed
568 The id of SL::DB::File
578 The actual file object is retrieved. The id of the object is needed.
586 The id of SL::DB::File
590 =item C<get_all PARAMS>
592 All last versions of file data objects which are related to an ERP-Document,Part,Customer,Vendor,... are retrieved.
600 The Id of the ERP-object.
604 The Type of the ERP-object like "sales_quotation". A clear mapping to the class/model exists in the controller.
608 The type may be "documents", "attachments" or "images". This parameter is optional.
612 The name of the file . This parameter is optional.
616 The MIME type of the file . This parameter is optional.
620 The type may be "created", "uploaded" or email sources or scanner soureces. This parameter is optional.
624 An optional parameter in which sorting the files are retrieved. Default is decrementing itime and ascending filename
628 =item C<get_all_versions PARAMS>
630 All versions of file data objects which are related to an ERP-Document,Part,Customer,Vendor,... are retrieved.
631 If only the versions of one file are wanted, additional parameter like file_name must be set.
632 If the param C<dbfile> set, only the versions of this file are returned.
634 Available C<PARAMS> ar the same as L<get_all>
638 =item C<get_all_count PARAMS>
640 The count of available files is returned.
641 Available C<PARAMS> ar the same as L<get_all>
644 =item C<get_content PARAMS>
646 The data of a file can retrieved. A reference to the data is returned.
654 The id of SL::DB::File
658 If no Id exists the object SL::DB::File as param.
662 =item C<get_file_path PARAMS>
664 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
665 it is in the responsibility of the backend to create a tempory session file.
673 The id of SL::DB::File
677 If no Id exists the object SL::DB::File as param.
681 =item C<get_other_sources>
683 A helpful method to get the sources for scanner and email from UserPreferences. This method is need from SL::Controller::File
685 =item C<sync_from_backend>
687 For Backends which may be changed outside of kivitendo a synchronization of the database is done.
688 This sync must be triggered by a periodical task.
696 The synchronization is done file_type by file_type.
704 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>