1 package SL::File::Backend::Filesystem;
 
   5 use parent qw(SL::File::Backend);
 
  10 use File::Path qw(make_path);
 
  17   my ($self, %params) = @_;
 
  18   die "no dbfile in backend delete" unless $params{dbfile};
 
  19   my $last_version  = $params{dbfile}->backend_data;
 
  20   my $first_version = 1;
 
  21   $last_version     = 0                               if $params{last};
 
  22   $last_version     = $params{dbfile}->backend_data-1 if $params{all_but_notlast};
 
  23   $last_version     = $params{version}                if $params{version};
 
  24   $first_version    = $params{version}                if $params{version};
 
  26   if ($last_version > 0 ) {
 
  27     for my $version ( $first_version..$last_version) {
 
  28       my $file_path = $self->_filesystem_path($params{dbfile},$version);
 
  31     if ($params{version}) {
 
  32       for my $version ( $last_version+1 .. $params{dbfile}->backend_data) {
 
  33         my $from = $self->_filesystem_path($params{dbfile},$version);
 
  34         my $to   = $self->_filesystem_path($params{dbfile},$version - 1);
 
  35         die "file not exists in backend delete" unless -f $from;
 
  38       $params{dbfile}->backend_data($params{dbfile}->backend_data-1);
 
  40     elsif ($params{all_but_notlast}) {
 
  41       my $from = $self->_filesystem_path($params{dbfile},$params{dbfile}->backend_data);
 
  42       my $to   = $self->_filesystem_path($params{dbfile},1);
 
  43       die "file not exists in backend delete" unless -f $from;
 
  45       $params{dbfile}->backend_data(1);
 
  47       $params{dbfile}->backend_data(0);
 
  49     unless ($params{dbfile}->backend_data) {
 
  50       my $dir_path = $self->_filesystem_path($params{dbfile});
 
  54     my $file_path = $self->_filesystem_path($params{dbfile},$params{dbfile}->backend_data);
 
  55     die "file not exists in backend delete" unless -f $file_path;
 
  57     $params{dbfile}->backend_data($params{dbfile}->backend_data-1);
 
  66   my ($self, %params) = @_;
 
  67   die 'dbfile not exists' unless $params{dbfile};
 
  68   my $dbfile = $params{dbfile};
 
  69   die 'no file contents' unless $params{file_path} || $params{file_contents};
 
  71   # Do not save and do not create a new version of the document if file size of last version is the same.
 
  72   if ($dbfile->source eq 'created' && $self->get_version_count(dbfile => $dbfile)) {
 
  73     my $new_file_size  = $params{file_path} ? stat($params{file_path})->size : length($params{file_contents});
 
  74     my $last_file_size = stat($self->_filesystem_path($dbfile))->size;
 
  76     return 1 if $last_file_size == $new_file_size;
 
  79   $dbfile->backend_data(0) unless $dbfile->backend_data;
 
  80   $dbfile->backend_data($dbfile->backend_data*1+1);
 
  83   my $tofile = $self->_filesystem_path($dbfile);
 
  84   if ($params{file_path} && -f $params{file_path}) {
 
  85     File::Copy::copy($params{file_path}, $tofile);
 
  87   elsif ($params{file_contents}) {
 
  88     open(OUT, "> " . $tofile);
 
  89     print OUT $params{file_contents};
 
  93     utime($params{mtime}, $params{mtime}, $tofile);
 
  98 sub get_version_count {
 
  99   my ($self, %params) = @_;
 
 100   die "no dbfile" unless $params{dbfile};
 
 101   return $params{dbfile}->backend_data//0 * 1;
 
 105   my ($self, %params) = @_;
 
 106   die "no dbfile" unless $params{dbfile};
 
 107   die "unknown version" if $params{version} &&
 
 108                           ($params{version} < 0 || $params{version} > $params{dbfile}->backend_data);
 
 109   my $path = $self->_filesystem_path($params{dbfile}, $params{version});
 
 111   die "No file found at $path. Expected: $params{dbfile}{file_name}, file.id: $params{dbfile}{id}" if !-f $path;
 
 113   my $dt = DateTime->from_epoch(epoch => stat($path)->mtime, time_zone => $::locale->get_local_time_zone()->name, locale => $::lx_office_conf{system}->{language})->clone();
 
 118   my ($self, %params) = @_;
 
 119   die "no dbfile" unless $params{dbfile};
 
 120   my $path = $self->_filesystem_path($params{dbfile},$params{version});
 
 122   die "No file found at $path. Expected: $params{dbfile}{file_name}, file.id: $params{dbfile}{id}" if !-f $path;
 
 128   my ($self, %params) = @_;
 
 129   my $path = $self->get_filepath(%params);
 
 130   return "" unless $path;
 
 131   my $contents = File::Slurp::read_file($path);
 
 136   return 0 unless $::instance_conf->get_doc_files;
 
 137   return 0 unless $::lx_office_conf{paths}->{document_path};
 
 138   return 0 unless -d $::lx_office_conf{paths}->{document_path};
 
 142 sub sync_from_backend {
 
 143   my ($self, %params) = @_;
 
 144   my @query = (file_type => $params{file_type});
 
 145   push @query, (file_name => $params{file_name}) if $params{file_name};
 
 146   push @query, (mime_type => $params{mime_type}) if $params{mime_type};
 
 147   push @query, (source    => $params{source})    if $params{source};
 
 149   my $sortby = $params{sort_by} || 'itime DESC,file_name ASC';
 
 151   my @files = @{ SL::DB::Manager::File->get_all(query => [@query], sort_by => $sortby) };
 
 153     $main::lxdebug->message(LXDebug->DEBUG2(), "file id=" . $_->id." version=".$_->backend_data);
 
 154     my $newversion = $_->backend_data;
 
 155     for my $version ( reverse 1 .. $_->backend_data ) {
 
 156       my $path = $self->_filesystem_path($_, $version);
 
 157       $main::lxdebug->message(LXDebug->DEBUG2(), "path=".$path." exists=".( -f $path?1:0));
 
 159       $newversion = $version - 1;
 
 161     $main::lxdebug->message(LXDebug->DEBUG2(), "newversion=".$newversion." version=".$_->backend_data);
 
 162     if ( $newversion < $_->backend_data ) {
 
 163       $_->backend_data($newversion);
 
 164       $_->save   if $newversion >  0;
 
 165       $_->delete if $newversion <= 0;
 
 175 sub _filesystem_path {
 
 176   my ($self, $dbfile, $version) = @_;
 
 178   die "No files backend enabled" unless $::instance_conf->get_doc_files || $::lx_office_conf{paths}->{document_path};
 
 180   # use filesystem with depth 3
 
 181   $version    = $dbfile->backend_data if !$version || $version < 1 || $version > $dbfile->backend_data;
 
 182   my $iddir   = sprintf("%04d", $dbfile->id % 1000);
 
 183   my $path    = File::Spec->catdir($::lx_office_conf{paths}->{document_path}, $::auth->client->{id}, $iddir, $dbfile->id);
 
 185     File::Path::make_path($path, { chmod => 0770 });
 
 187   return $path if !$version;
 
 188   return File::Spec->catdir($path, $dbfile->id . '_' . $version);
 
 201 SL::File::Backend::Filesystem  - Filesystem class for file storage backend
 
 205 See the synopsis of L<SL::File::Backend>.
 
 209 This specific storage backend use a Filesystem which is only accessed by this interface.
 
 210 This is the big difference to the Webdav backend where the files can be accessed without the control of that backend.
 
 211 This backend use the database id of the SL::DB::File object as filename. The filesystem has up to 1000 subdirectories
 
 212 to store the files not to flat in the filesystem. In this Subdirectories for each file an additional subdirectory exists
 
 213 for the versions of this file.
 
 215 The Versioning is done via a Versionnumber which is incremented by one for each version.
 
 216 So the Version 2 of the file with the database id 4 is stored as path {root}/0004/4/4_2.
 
 221 See methods of L<SL::File::Backend>.
 
 229 Martin Helmling E<lt>martin.helmling@opendynamic.deE<gt>