Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 2bb452ee

Von Martin Helmling mh@waldpark.octosoft.eu vor fast 8 Jahren hinzugefügt

  • ID 2bb452ee62e74c2a42113171eeb53de8d09bbbf5
  • Vorgänger fbcd5580
  • Nachfolger 8c7f25bc

Dateimanagement: Basiserweiterung

In diesem Commit sind die Anpassungen in der Mandantenkonfiguration
sowie die notwendigen Klassen und Controller.

Über eine Zwischenschicht wird das tatsächliche Backend (Dateien,WebDAV,ext.DMS,Datenbank etc) verborgen.

Unterschiede anzeigen:

SL/Controller/ClientConfig.pm
212 212
sub edit_form {
213 213
  my ($self) = @_;
214 214

  
215
  $::request->layout->use_javascript("${_}.js") for qw(jquery.selectboxes jquery.multiselect2side);
215
  $::request->layout->use_javascript("${_}.js") for qw(jquery.selectboxes jquery.multiselect2side kivi.File);
216 216

  
217 217
  $self->render('client_config/form', title => t8('Client Configuration'),
218 218
                make_chart_title     => sub { $_[0]->accno . '--' . $_[0]->description },
SL/DB/File.pm
1
# This file has been auto-generated only because it didn't exist.
2
# Feel free to modify it at will; it will not be overwritten automatically.
3

  
4
package SL::DB::File;
5

  
6
use strict;
7

  
8
use SL::DB::MetaSetup::File;
9
use SL::DB::Manager::File;
10

  
11
__PACKAGE__->meta->initialize;
12

  
13

  
14
1;
15
__END__
16

  
17
=pod
18

  
19
=encoding utf8
20

  
21
=head1 NAME
22

  
23
  SL::DB::File - Databaseclass for File
24

  
25
=head1 SYNOPSIS
26

  
27
  use SL::DB::File;
28

  
29
  # synopsis...
30

  
31
=head1 DESCRIPTION
32

  
33
  # longer description.
34

  
35

  
36
=head1 INTERFACE
37

  
38

  
39
=head1 DEPENDENCIES
40

  
41

  
42
=head1 SEE ALSO
43

  
44
=head1 AUTHOR
45

  
46
  Werner Hahn E<lt>wh@futureworldsearch.netE<gt>
47

  
48
=cut
SL/DB/Helper/ALL.pm
51 51
use SL::DB::EmailJournalAttachment;
52 52
use SL::DB::Employee;
53 53
use SL::DB::Exchangerate;
54
use SL::DB::File;
54 55
use SL::DB::Finanzamt;
55 56
use SL::DB::FollowUp;
56 57
use SL::DB::FollowUpAccess;
SL/DB/Helper/Mappings.pm
135 135
  email_journal_attachments      => 'EmailJournalAttachment',
136 136
  employee                       => 'employee',
137 137
  exchangerate                   => 'exchangerate',
138
  files                          => 'file',
138 139
  finanzamt                      => 'finanzamt',
139 140
  follow_up_access               => 'follow_up_access',
140 141
  follow_up_links                => 'follow_up_link',
SL/DB/Manager/File.pm
1
# This file has been auto-generated only because it didn't exist.
2
# Feel free to modify it at will; it will not be overwritten automatically.
3

  
4
package SL::DB::Manager::File;
5

  
6
use strict;
7

  
8
use parent qw(SL::DB::Helper::Manager);
9
use SL::DB::Helper::Sorted;
10

  
11
sub object_class { 'SL::DB::File' }
12

  
13
__PACKAGE__->make_manager_methods;
14

  
15
1;
SL/DB/MetaSetup/Default.pm
45 45
  datev_check_on_purchase_invoice           => { type => 'boolean', default => 'true' },
46 46
  datev_check_on_sales_invoice              => { type => 'boolean', default => 'true' },
47 47
  disabled_price_sources                    => { type => 'array' },
48
  doc_database                              => { type => 'boolean', default => 'false' },
49
  doc_delete_printfiles                     => { type => 'boolean', default => 'false' },
50
  doc_files                                 => { type => 'boolean', default => 'false' },
51
  doc_files_rootpath                        => { type => 'text', default => '' },
52
  doc_max_filesize                          => { type => 'integer', default => 1000000 },
53
  doc_storage                               => { type => 'boolean', default => 'false' },
54
  doc_storage_for_attachments               => { type => 'text', default => 'Filesystem' },
55
  doc_storage_for_documents                 => { type => 'text', default => 'Filesystem' },
56
  doc_storage_for_images                    => { type => 'text', default => 'Filesystem' },
57
  doc_webdav                                => { type => 'boolean', default => 'false' },
48 58
  dunning_ar                                => { type => 'integer' },
49 59
  dunning_ar_amount_fee                     => { type => 'integer' },
50 60
  dunning_ar_amount_interest                => { type => 'integer' },
SL/DB/MetaSetup/File.pm
1
# This file has been auto-generated. Do not modify it; it will be overwritten
2
# by rose_auto_create_model.pl automatically.
3
package SL::DB::File;
4

  
5
use strict;
6

  
7
use parent qw(SL::DB::Object);
8

  
9
__PACKAGE__->meta->table('files');
10

  
11
__PACKAGE__->meta->columns(
12
  backend      => { type => 'text' },
13
  backend_data => { type => 'text' },
14
  description  => { type => 'text' },
15
  file_name    => { type => 'text', not_null => 1 },
16
  file_type    => { type => 'text', not_null => 1 },
17
  id           => { type => 'serial', not_null => 1 },
18
  itime        => { type => 'timestamp', default => 'now()' },
19
  mime_type    => { type => 'text', not_null => 1 },
20
  mtime        => { type => 'timestamp' },
21
  object_id    => { type => 'integer', not_null => 1 },
22
  object_type  => { type => 'text', not_null => 1 },
23
  source       => { type => 'text', not_null => 1 },
24
  title        => { type => 'varchar', length => 45 },
25
);
26

  
27
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
28

  
29
__PACKAGE__->meta->allow_inline_column_values(1);
30

  
31
1;
32
;
SL/Dev/File.pm
1
package SL::Dev::File;
2

  
3
use strict;
4
use base qw(Exporter);
5
our @EXPORT = qw(create_scanned create_uploaded create_created get_all_count get_all get_all_versions delete_all);
6

  
7
use SL::DB::File;
8

  
9
sub create_scanned {
10
  my (%params) = @_;
11
  $params{source}    = 'scanner1';
12
  $params{file_type} = 'document';
13
  $params{file_path} = '/var/tmp/'.$params{file_name} if !$params{file_path};
14
  open(OUT,"> ".$params{file_path});
15
  print OUT $params{file_contents};
16
  close(OUT);
17
  delete $params{file_contents};
18
  my $file = _create_file(%params);
19
  unlink($params{file_path});
20
  return $file;
21
}
22

  
23
sub create_uploaded {
24
  my (%params) = @_;
25
  $params{source}    = 'uploaded';
26
  $params{file_type} = 'attachment';
27
  return _create_file(%params);
28
}
29

  
30
sub create_created {
31
  my (%params) = @_;
32
  $params{source}    = 'created';
33
  $params{file_type} = 'document';
34
  return _create_file(%params);
35
}
36

  
37
sub _create_file {
38
  my (%params) = @_;
39

  
40
  my $fileobj = SL::File->save(
41
    object_id          => 1,
42
    object_type        => 'sales_order',
43
    mime_type          => 'text/plain',
44
    description        => 'Test File',
45
    file_type          => $params{file_type},
46
    source             => $params{source},
47
    file_name          => $params{file_name},
48
    file_contents      => $params{file_contents},
49
    file_path          => $params{file_path}
50
  );
51
  return $fileobj;
52
}
53

  
54
sub get_all_count {
55
  my ($class,%params) = @_;
56
  $params{object_id}   = 1;
57
  $params{object_type} = 'sales_order';
58
  return SL::File->get_all_count(%params);
59
}
60

  
61
sub get_all {
62
  my ($class,%params) = @_;
63
  $params{object_id}   = 1;
64
  $params{object_type} = 'sales_order';
65
  SL::File->get_all(%params);
66
}
67

  
68
sub get_all_versions {
69
  my ($class,%params) = @_;
70
  $params{object_id}   = 1;
71
  $params{object_type} = 'sales_order';
72
  SL::File->get_all_versions(%params);
73
}
74

  
75
sub delete_all {
76
  my ($class,%params) = @_;
77
  $params{object_id}   = 1;
78
  $params{object_type} = 'sales_order';
79
  SL::File->delete_all(%params);
80
}
81
1;
82

  
83
__END__
84

  
85
=head1 NAME
86

  
87
SL::Dev::File - create file objects for testing, with minimal defaults
88

  
89
=head1 FUNCTIONS
90

  
91
=head2 C<create_scanned %PARAMS>
92

  
93
=head2 C<create_uploaded %PARAMS>
94

  
95
=head2 C<create_created %PARAMS>
96

  
97
=head2 C<delete_all>
98

  
99
=head1 AUTHOR
100

  
101
Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
102

  
103
=cut
SL/File.pm
1
package SL::File;
2

  
3
use strict;
4

  
5
use parent qw(Rose::Object);
6

  
7
use Clone qw(clone);
8
use SL::File::Backend;
9
use SL::File::Object;
10
use SL::DB::History;
11
use SL::DB::File;
12
use SL::Helper::UserPreferences;
13
use SL::JSON;
14

  
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;
20

  
21
sub get {
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);
28
}
29

  
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);
37
}
38

  
39
sub get_all {
40
  my ($self, %params) = @_;
41

  
42
  my @files;
43
  return @files unless $params{object_type};
44
  return @files unless defined($params{object_id});
45

  
46
  my @query = (
47
    object_id   => $params{object_id},
48
    object_type => $params{object_type}
49
  );
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};
54

  
55
  my $sortby = $params{sort_by} || 'itime DESC,file_name ASC';
56

  
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;
59
}
60

  
61
sub get_all_versions {
62
  my ($self, %params) = @_;
63
  my @versionobjs;
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);
67
  } else {
68
    @fileobjs = $self->get_all(%params);
69
  }
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));
77
        eval {
78
          my $clone = clone($fileobj);
79
          $clone->version($maxversion-$version+1);
80
          $clone->newest(0);
81
          $main::lxdebug->message(LXDebug->DEBUG2(), "clone version=".$clone->version." mtime=". $clone->mtime);
82
          push @versionobjs, $clone;
83
          1;
84
        }
85
      }
86
    }
87
  }
88
  return @versionobjs;
89
}
90

  
91
sub get_all_count {
92
  my ($self, %params) = @_;
93
  return 0 unless $params{object_type};
94

  
95
  my @query = (
96
    object_id   => $params{object_id},
97
    object_type => $params{object_type}
98
  );
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};
103

  
104
  my $cnt = SL::DB::Manager::File->get_all_count(query => [@query]);
105
  return $cnt;
106
}
107

  
108
sub delete_all {
109
  my ($self, %params) = @_;
110
  return 0 unless defined($params{object_id}) || $params{object_type};
111
  my $files = SL::DB::Manager::File->get_all(
112
    query => [
113
      object_id   => $params{object_id},
114
      object_type => $params{object_type}
115
    ]
116
  );
117
  foreach my $file (@{$files}) {
118
    $params{dbfile} = $file;
119
    $self->delete(%params);
120
  }
121
}
122

  
123
sub delete {
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);
127
  if (!$rc) {
128
    my $err = SL::DB->client->error;
129
    die (ref $err?$$err:$err);
130
  }
131
  return $rc;
132
}
133

  
134
sub _delete {
135
  my ($self, %params) = @_;
136
  $params{dbfile} = SL::DB::Manager::File->get_first(query => [id => $params{id}]) if !$params{dbfile};
137

  
138
  my $backend = $self->_get_backend($params{dbfile}->backend);
139
  if ( $params{dbfile}->file_type eq 'document' && $params{dbfile}->source ne 'created')
140
  {
141
    ## must unimport
142
    my $hist = SL::DB::Manager::History->get_first(
143
      where => [
144
        addition  => 'IMPORT',
145
        trans_id  => $params{dbfile}->object_id,
146
        what_done => $params{dbfile}->id
147
      ]
148
    );
149

  
150
    if ($hist) {
151
      if (!$main::auth->assert('import_ar | import_ap', 1)) {
152
        die \'no permission to unimport';
153
      }
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');
158
      $hist->save;
159
    }
160
  }
161
  if ($backend->delete(%params)) {
162
    my $do_delete = 0;
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;
167
      } else {
168
        $do_delete = 1;
169
      }
170
    } else {
171
      $do_delete = 1;
172
    }
173
    $params{dbfile}->delete if $do_delete;
174
    return 1;
175
  }
176
  return 0;
177
}
178

  
179
sub save {
180
  my ($self, %params) = @_;
181

  
182
  my $obj = SL::DB->client->with_transaction(\&_save, $self, %params);
183
  if (!$obj) {
184
    my $err = SL::DB->client->error;
185
    die (ref $err?$$err:$err);
186
  }
187
  return $obj;
188
}
189

  
190
sub _save {
191
  my ($self, %params) = @_;
192
  my $file = $params{dbfile};
193
  my $exists = 0;
194

  
195
  if ($params{id}) {
196
    $file = SL::DB::File->new(id => $params{id})->load;
197
    die \'dbfile not exists'     unless $file;
198
  } elsif (!$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});
202

  
203
    $exists = $self->get_all_count(%params);
204
    die 'filename still exist' if $exists && $params{fail_if_exists};
205
    if ($exists) {
206
      my ($obj1) = $self->get_all(%params);
207
      $file = $obj1->db_file;
208
    } else {
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},
219
      );
220
    }
221
  } else {
222
    $exists = 1;
223
  }
224
  if ($exists) {
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};
230
  }
231
  if ( !$file->backend ) {
232
    $file->backend($self->_get_backend_by_file_type($file));
233
    # load itime for new file
234
    $file->save->load;
235
  }
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);
240

  
241
  $file->mtime(DateTime->now_local);
242
  $file->save;
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
250
    )->save();
251
  }
252
  return $params{obj} if $params{dbfile} && $params{obj};
253
  return SL::File::Object->new(db_file => $file, id => $file->id, loaded => 1);
254
}
255

  
256
sub rename {
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;
262

  
263
  $main::lxdebug->message(LXDebug->DEBUG2(), "rename id=" . $file->id . " to=" . $params{to});
264
  if ($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}
272
                                                ) > 0;
273

  
274
    my $backend = $self->_get_backend($file->backend);
275
    $backend->rename(dbfile => $file) if $backend;
276
    $file->file_name($params{to});
277
    $file->save;
278
  }
279
  return RENAME_OK;
280
}
281

  
282
sub get_backend_class {
283
  my ($self, $backendname) = @_;
284
  die "no backend name set" unless $backendname;
285
  $self->_get_backend($backendname);
286
}
287

  
288
sub get_other_sources {
289
  my ($self) = @_;
290
  my $pref = SL::Helper::UserPreferences->new(namespace => 'file_sources');
291
  $pref->login("#default#");
292
  my @sources;
293
  foreach my $tuple (@{ $pref->get_all() }) {
294
    my %lkeys  = %{ SL::JSON::from_json($tuple->{value}) };
295
    my $source = {
296
      'name'        => $tuple->{key},
297
      'description' => $lkeys{desc},
298
      'directory'   => $lkeys{dir}
299
    };
300
    push @sources, $source;
301
  }
302
  return @sources;
303
}
304

  
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);
313
}
314

  
315
#
316
# internal
317
#
318
sub _get_backend {
319
  my ($self, $backend_name) = @_;
320
  my $class = 'SL::File::Backend::' . $backend_name;
321
  my $obj   = undef;
322
  eval {
323
    eval "require $class";
324
    $obj = $class->new;
325
    die \'backend not enabled' unless $obj->enabled;
326
    1;
327
  } or do {
328
    die \'backend class not found';
329
  };
330
  return $obj;
331
}
332

  
333
sub _get_backend_by_file_type {
334
  my ($self, $dbfile) = @_;
335

  
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';
341
  return "Filesystem";
342
}
343

  
344
1;
345

  
346
__END__
347

  
348
=pod
349

  
350
=encoding utf8
351

  
352
=head1 NAME
353

  
354
SL::File - The intermediate Layer for handling files
355

  
356
=head1 SYNOPSIS
357

  
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
360

  
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');
367

  
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
374
                      );
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
380
                                );
381

  
382
  # Alternativelly some operation can be done with the filemangement object wrapper
383
  # and additional oparations see L<SL::File::Object>
384

  
385
=head1 OVERVIEW
386

  
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.
390

  
391
There are three types of files:
392

  
393
=over 2
394

  
395
=item - documents,
396

  
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.
400

  
401
=item - attachments,
402

  
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.
405

  
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.
409

  
410
=item - images,
411

  
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
414

  
415
=back
416

  
417
For each type of files the backend can configured in L<SL::Controller::ClientConfig>.
418

  
419
The files have also the parameter C<Source>:
420

  
421
=over 2
422

  
423
=item - created, generated by LaTeX
424

  
425
=item - uploaded
426

  
427
=item - scanner, import from scanner
428

  
429
( or scanner1, scanner2 if there are different scanner, be configurable via UserPreferences )
430

  
431
=item - email, received by email and imported by hand or automatic.
432

  
433
=back
434

  
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.
437

  
438
The scanner and email import must be configured  via Table UserPreferences:
439

  
440
=begin text
441

  
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"}
446

  
447
=end text
448

  
449
.
450

  
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.
453

  
454
The interface SL::File:Object encapsulate SL::DB:File, see L<SL::DB::Object>
455

  
456
The storage backends are extra classes which depends from L<SL::File::Backend>.
457
So additional backend classes can be added.
458

  
459
The implementation of versioning is done in the different backends.
460

  
461
=head1 METHODS
462

  
463
=over 4
464

  
465
=item C<save>
466

  
467
Creates a new SL::DB:File object or save an existing object for a specific backend depends of the C<file_type>
468
and config, like
469

  
470
=begin text
471

  
472
          SL::File->save(
473
                         object_id    => $self->object_id,
474
                         object_type  => $self->object_type,
475
                         content_type => 'application/pdf'
476
                        );
477

  
478
=end text
479

  
480
.
481

  
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.
484

  
485
Available C<PARAMS>:
486

  
487
=over 4
488

  
489
=item C<id>
490

  
491
The id of SL::DB::File for an existing file
492

  
493
=item C<object_id>
494

  
495
The Id of the ERP-object for a new file.
496

  
497
=item C<object_type>
498

  
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.
500

  
501
=item C<file_type>
502

  
503
The type may be "documents", "attachments" or "images" for a new file.
504

  
505
=item C<source>
506

  
507
The type may be "created", "uploaded" or email sources or scanner sources for a new file.
508

  
509
=item C<file_name>
510

  
511
The file_name of the file for a new file. This name is used in the WebGUI and as name for download.
512

  
513
=item C<mime_type>
514

  
515
The mime_type of a new file. This is used for downloading or for email attachments.
516

  
517
=item C<description> or C<title>
518

  
519
The description or title of a new file. This must be discussed if this attribute is needed.
520

  
521
=back
522

  
523
=item C<delete PARAMS>
524

  
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.
527

  
528
Available C<PARAMS>:
529

  
530
=over 4
531

  
532
=item C<id>
533

  
534
The id of SL::DB::File
535

  
536
=item C<dbfile>
537

  
538
As alternative if the SL::DB::File as object is available.
539

  
540
=back
541

  
542
=item C<delete_all PARAMS>
543

  
544
All file data of an ERP-object is deleted in the backend.
545

  
546
=over 4
547

  
548
=item C<object_id>
549

  
550
The Id of the ERP-object.
551

  
552
=item C<object_type>
553

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

  
556
=back
557

  
558
=item C<rename PARAMS>
559

  
560
The Filename of the file is changed
561

  
562
Available C<PARAMS>:
563

  
564
=over 4
565

  
566
=item C<id>
567

  
568
The id of SL::DB::File
569

  
570
=item C<to>
571

  
572
The new filename
573

  
574
=back
575

  
576
=item C<get PARAMS>
577

  
578
The actual file object is retrieved. The id of the object is needed.
579

  
580
Available C<PARAMS>:
581

  
582
=over 4
583

  
584
=item C<id>
585

  
586
The id of SL::DB::File
587

  
588
=back
589

  
590
=item C<get_all PARAMS>
591

  
592
All last versions of file data objects which are related to an ERP-Document,Part,Customer,Vendor,... are retrieved.
593

  
594
Available C<PARAMS>:
595

  
596
=over 4
597

  
598
=item C<object_id>
599

  
600
The Id of the ERP-object.
601

  
602
=item C<object_type>
603

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

  
606
=item C<file_type>
607

  
608
The type may be "documents", "attachments" or "images". This parameter is optional.
609

  
610
=item C<file_name>
611

  
612
The name of the file . This parameter is optional.
613

  
614
=item C<mime_type>
615

  
616
The MIME type of the file . This parameter is optional.
617

  
618
=item C<source>
619

  
620
The type may be "created", "uploaded" or email sources or scanner soureces. This parameter is optional.
621

  
622
=item C<sort_by>
623

  
624
An optional parameter in which sorting the files are retrieved. Default is decrementing itime and ascending filename
625

  
626
=back
627

  
628
=item C<get_all_versions PARAMS>
629

  
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.
633

  
634
Available C<PARAMS> ar the same as L<get_all>
635

  
636

  
637

  
638
=item C<get_all_count PARAMS>
639

  
640
The count of available files is returned.
641
Available C<PARAMS> ar the same as L<get_all>
642

  
643

  
644
=item C<get_content PARAMS>
645

  
646
The data of a file can retrieved. A reference to the data is returned.
647

  
648
Available C<PARAMS>:
649

  
650
=over 4
651

  
652
=item C<id>
653

  
654
The id of SL::DB::File
655

  
656
=item C<dbfile>
657

  
658
If no Id exists the object SL::DB::File as param.
659

  
660
=back
661

  
662
=item C<get_file_path PARAMS>
663

  
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.
666

  
667
Available C<PARAMS>:
668

  
669
=over 4
670

  
671
=item C<id>
672

  
673
The id of SL::DB::File
674

  
675
=item C<dbfile>
676

  
677
If no Id exists the object SL::DB::File as param.
678

  
679
=back
680

  
681
=item C<get_other_sources>
682

  
683
A helpful method to get the sources for scanner and email from UserPreferences. This method is need from SL::Controller::File
684

  
685
=item C<sync_from_backend>
686

  
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.
689

  
690
Needed C<PARAMS>:
691

  
692
=over 4
693

  
694
=item C<file_type>
695

  
696
The synchronization is done file_type by file_type.
697

  
698
=back
699

  
700
=back
701

  
702
=head1 AUTHOR
703

  
704
Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
705

  
706
=cut
707

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

  
3
use strict;
4

  
5
use parent qw(Rose::Object);
6

  
7
sub store { die 'store needs to be implemented' }
8

  
9
sub delete { die 'delete needs to be implemented' }
10

  
11
sub rename { die 'rename needs to be implemented' }
12

  
13
sub get_content { die 'get_content needs to be implemented' }
14

  
15
sub get_filepath { die 'get_filepath needs to be implemented' }
16

  
17
sub get_mtime { die 'get_mtime needs to be implemented' }
18

  
19
sub get_version_count { die 'get_version_count needs to be implemented' }
20

  
21
sub enabled { 0; }
22

  
23
sub sync_from_backend { }
24

  
25
1;
26

  
27
__END__
28

  
29
=pod
30

  
31
=encoding utf8
32

  
33
=head1 NAME
34

  
35
SL::File::Backend  - Base class for file storage backend
36

  
37
=head1 SYNOPSIS
38

  
39
See the synopsis of L<SL::File> and L<SL::File::Object>
40

  
41
=head1 OVERVIEW
42

  
43
The most methods must be overridden by the specific storage backend
44

  
45
See also the overview of L<SL::File> and L<SL::File::Object>.
46

  
47

  
48
=head1 METHODS
49

  
50
=over 4
51

  
52
=item C<store PARAMS>
53

  
54
The file data is stored in the backend.
55

  
56
Available C<PARAMS>:
57

  
58
=over 4
59

  
60
=item C<dbfile>
61

  
62
The object SL::DB::File as param.
63

  
64
=item C<file_contents>
65

  
66
The data of the file to store
67

  
68
=item C<file_path>
69

  
70
If the data is still in a file, the contents of this file is copied.
71

  
72
=back
73

  
74
If both parameter C<file_contents> and C<file_path> exists,
75
the backend is responsible in which way the contents is fetched.
76

  
77
If the file exists the backend is responsible to decide to save a new version of the file or override the
78
latest existing file.
79

  
80
=item C<delete PARAMS>
81

  
82
The file data is deleted in the backend.
83

  
84
Available C<PARAMS>:
85

  
86
=over 4
87

  
88
=item C<dbfile>
89

  
90
The object SL::DB::File as param.
91

  
92
=item C<last>
93

  
94
If this parameter is set only the latest version of the file are deleted.
95

  
96
=item C<all_but_notlast>
97

  
98
If this parameter is set all versions of the file are deleted except the latest version.
99

  
100
If none of the two parameters C<all_versions> or C<all__but_notlast> is set
101
all version of the file are deleted.
102

  
103
=back
104

  
105
=item C<rename PARAMS>
106

  
107
The Filename of the file is changed. If the backend is not dependant from the filename
108
nothing must happens. The rename must work on all versions of the file.
109

  
110
Available C<PARAMS>:
111

  
112
=over 4
113

  
114
=item C<dbfile>
115

  
116
The object SL::DB::File as param.
117

  
118
=back
119

  
120
=item C<get_version_count PARAMS>
121

  
122
The count of the available versions of a file will returned.
123
The versions are numbered from 1 up to the returned value
124

  
125
Available C<PARAMS>:
126

  
127
=over 4
128

  
129
=item C<dbfile>
130

  
131
=back
132

  
133
=item C<get_mtime PARAMS>
134

  
135
Available C<PARAMS>:
136

  
137
=over 4
138

  
139
=item C<dbfile>
140

  
141
The object SL::DB::File as param.
142

  
143
=item C<version>
144

  
145
The version number of the file for which the modification timestamp is wanted.
146
If no version set or version is 0 , the mtime of the latest version is returned.
147

  
148
=back
149

  
150
=item C<get_content PARAMS>
151

  
152
For downloading or printing the real data need to retrieve.
153
A reference of the data must be returned.
154

  
155
Available C<PARAMS>:
156

  
157
=over 4
158

  
159
=item C<dbfile>
160

  
161
The object SL::DB::File as param.
162

  
163
=back
164

  
165
=item C<get_file_path PARAMS>
166

  
167
If the backend has files as storage, the file path can returned.
168
If a file is not available in the backend a temporary file must be created with the contents.
169

  
170
Available C<PARAMS>:
171

  
172
=over 4
173

  
174
=item C<dbfile>
175

  
176
The object SL::DB::File as param.
177

  
178
=back
179

  
180
=item C<enabled>
181

  
182
returns 1 if the backend is enabled and has all config to work.
183
In other cases it must return 0
184

  
185
=item C<sync_from_backend>
186

  
187
For Backends which may be changed outside of kivitendo a synchronization of the database is done.
188
Normally the backend is responsible to actualise the data if it needed.
189
This interface can be used if a long work must be done and runs in a extra task.
190

  
191
=back
192

  
193
=head1 SEE ALSO
194

  
195
L<SL::File>, L<SL::File::Object>
196

  
197
=head1 AUTHOR
198

  
199
Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
200

  
201
=cut
202

  
203

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

  
3
use strict;
4
use parent qw(Rose::Object);
5
use DateTime;
6

  
7
use Rose::Object::MakeMethods::Generic (
8
  scalar => [ qw() ],
9
  'scalar --get_set_init' => [ qw(db_file loaded id version newest) ],
10
);
11

  
12
#use SL::DB::Helper::Attr;
13
#__PACKAGE__->_as_timestamp('mtime');
14
# wie wird das mit dem Attr Helper gemacht damit er bei nicht DB Objekten auch geht?
15

  
16
sub mtime_as_timestamp_s {
17
  $::locale->format_date_object($_[0]->mtime, precision => 'second');
18
}
19

  
20
# wrapper methods
21

  
22
sub itime {
23
  $_[0]->loaded_db_file->itime;
24
}
25

  
26
sub file_name {
27
  $_[0]->loaded_db_file->file_name;
28
}
29

  
30
sub file_type {
31
  $_[0]->loaded_db_file->file_type;
32
}
33

  
34
sub file_name {
35
  $_[0]->loaded_db_file->file_name;
36
}
37

  
38
sub object_type {
39
  $_[0]->loaded_db_file->object_type;
40
}
41

  
42
sub object_id {
43
  $_[0]->loaded_db_file->object_id;
44
}
45

  
46
sub mime_type {
47
  $_[0]->loaded_db_file->mime_type;
48
}
49

  
50
sub file_title {
51
  $_[0]->loaded_db_file->title;
52
}
53

  
54
sub file_description {
55
  $_[0]->loaded_db_file->description;
56
}
57

  
58
sub backend {
59
  $_[0]->loaded_db_file->backend;
60
}
61

  
62
sub source {
63
  $_[0]->loaded_db_file->source;
64
}
65

  
66
# methods to go directly into the backends
67

  
68
sub get_file {
69
  $_[0]->backend_class->get_filepath(dbfile => $_[0]->loaded_db_file, version => $_[0]->version)
70
}
71

  
72
sub get_content {
73
  $_[0]->backend_class->get_content(dbfile => $_[0]->loaded_db_file,  version => $_[0]->version)
74
}
75

  
76
sub mtime {
77
  $_[0]->backend_class->get_mtime(dbfile => $_[0]->loaded_db_file, version => $_[0]->version)
78
}
79

  
80
sub version_count {
81
  $_[0]->backend_class->get_version_count(dbfile => $_[0]->loaded_db_file)
82
}
83

  
84
sub versions {
85
  SL::File->get_all_versions(dbfile => $_[0]->loaded_db_file)
86
}
87

  
88
sub save_contents {
89
  SL::File->save(dbfile => $_[0]->loaded_db_file, file_contents => $_[1] )
90
}
91

  
92
sub save_file {
93
  SL::File->save(dbfile => $_[0]->loaded_db_file, file_path => $_[1] )
94
}
95

  
96
sub delete {
97
  SL::File->delete(dbfile => $_[0]->loaded_db_file)
98
}
99

  
100
sub delete_last_version {
101
  SL::File->delete(dbfile => $_[0]->loaded_db_file, last => 1 )
102
}
103

  
104
sub purge {
105
  SL::File->delete(dbfile => $_[0]->loaded_db_file, all_but_notlast => 1 )
106
}
107

  
108
sub rename {
109
  SL::File->rename(dbfile => $_[0]->loaded_db_file, to => $_[1])
110
}
111

  
112
# internals
113

  
114
sub backend_class {
115
  SL::File->get_backend_class($_[0]->backend)
116
}
117

  
118

  
119
sub loaded_db_file {  # so, dass wir die nur einmal laden.
120
  if (!$_[0]->loaded) {
121
    $_[0]->db_file->load;
122
    $_[0]->loaded(1);
123
  }
124
  $_[0]->db_file;
125
}
126

  
127

  
128
sub init_db_file { die 'must always have a db file'; }
129
sub init_loaded  { 0 }
130
sub init_id      { 0 }
131
sub init_version { 0 }
132
sub init_newest  { 1 }
133

  
134
1;
135

  
136
__END__
137

  
138
=encoding utf-8
139

  
140
=head1 NAME
141

  
142
SL::File::Object - a filemangement object wrapper
143

  
144
=head1 SYNOPSIS
145

  
146
  use SL::File;
147

  
148
  my ($object) = SL::File->get_all(object_id   => $object_id,
149
                                   object_type => $object_type,
150
                                   file_type   => 'images',  # may be optional
151
                                   source      => 'uploaded' # may be optional
152
                                  );
153
# read attributes
154

  
155
  my $object_id   = $object->object_id,
156
  my $object_type = $object->object_type,
157
  my $file_type   = $object->file_type;
158
  my $file_name   = $object->file_name;
159
  my $mime_type   = $object->mime_type;
160

  
161
  my $mtime       = $object->mtime;
162
  my $itime       = $object->itime;
163
  my $id          = $object->id;
164
  my $newest      = $object->newest;
165

  
166
  my $versions    = $object->version_count;
167

  
168
  my @versionobjs = $object->versions;
169
  foreach ( @versionobjs ) {
170
    my $mtime    = $_->mtime;
171
    my $contents = $_->get_content;
172
  }
173

  
174
# update
175

  
176
  $object->rename("image1.png");
177
  $object->save_contents("new text");
178
  $object->save_file("/tmp/empty.png");
179
  $object->purge;
180
  $object->delete_last_version;
181
  $object->delete;
182

  
183
=head1 DESCRIPTION
184

  
185
This is a wrapper around a single object in the filemangement.
186

  
187
=head1 METHODS
188

  
189
Following methods are wrapper to read the attributes of L<SL::DB::File> :
190

  
191
=over 4
192

  
193
=item C<object_id>
194

  
195
=item C<object_type>
196

  
197
=item C<file_type>
198

  
199
=item C<file_name>
200

  
201
=item C<mime_name>
202

  
203
=item C<file_title>
204

  
205
=item C<file_description>
206

  
207
=item C<backend>
208

  
209
=item C<source>
210

  
211
=item C<itime>
212

  
213
=item C<id>
214

  
215
=back
216

  
217
Additional are there special methods. If the Object is created by SL::File::get_all_versions()
218
or by "$object->versions"
219
it has a version number. So the different mtime, filepath or content can be retrieved:
220

  
221
=over 4
222

  
223
=item C<mtime>
224

  
225
get the modification time of a (versioned) object
226

  
227
=item C<get_file>
228

  
229
get the full qualified file path of the (versioned) object
230

  
231
=item C<get_content>
232

  
233
get the content of the (versioned) object
234

  
235
=item C<version_count>
236

  
237
Get the available versions of the file
238

  
239
=item C<versions>
240

  
241
Returns an array of SL::File::Object objects with the available versions of the file, starting with the newest version.
242

  
243
=item C<newest>
244

  
245
If set this is the newest version of the file.
246

  
247
=item C<save_contents $contents>
248

  
249
Store a new contents to the file (as a new version).
250

  
251
=item C<save_file $filepath>
252

  
253
Store the content of an (absolute)file path to the file
254

  
255
=item C<delete>
256

  
257
Delete the file with all of his versions
258

  
259
=item C<delete_last_version>
260

  
261
Delete only the last version of the file with implicit decrement of the version_count.
262

  
263
=item C<purge>
264

  
265
Delete all old versions of the file. Only one version exist after purge. The version count is reset to 1.
266

  
267
=item C<rename $newfilename>
268

  
269
Renames the filename
270

  
271
=back
272

  
273
=head1 SEE ALSO
274

  
275
L<SL::File>
276

  
277
=head1 AUTHOR
278

  
279
Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>
280

  
281
=cut
... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet.

Auch abrufbar als: Unified diff