]> wagnertech.de Git - mfinanz.git/blobdiff - SL/Webdav.pm
Webdav: Framework um Dokumente im Webdav zu behandeln
[mfinanz.git] / SL / Webdav.pm
diff --git a/SL/Webdav.pm b/SL/Webdav.pm
new file mode 100644 (file)
index 0000000..9595512
--- /dev/null
@@ -0,0 +1,240 @@
+package SL::Webdav;
+
+use strict;
+use parent qw(Rose::Object);
+
+use Encode qw(decode);
+use File::Spec;
+use SL::Common;
+use SL::Webdav::File;
+use SL::Webdav::Object;
+use SL::Webdav::VersionScheme::Serial;
+use SL::Webdav::VersionScheme::Timestamp;
+
+use Rose::Object::MakeMethods::Generic (
+  scalar => [ qw(type number) ],
+  'scalar --get_set_init' => [ qw(version_scheme) ],
+);
+
+my %type_to_path = (
+  sales_quotation         => 'angebote',
+  sales_order             => 'bestellungen',
+  request_quotation       => 'anfragen',
+  purchase_order          => 'lieferantenbestellungen',
+  sales_delivery_order    => 'verkaufslieferscheine',
+  purchase_delivery_order => 'einkaufslieferscheine',
+  credit_note             => 'gutschriften',
+  invoice                 => 'rechnungen',
+  purchase_invoice        => 'einkaufsrechnungen',
+  part                    => 'waren',
+  service                 => 'dienstleistungen',
+  assembly                => 'erzeugnisse',
+);
+
+sub get_all_files {
+  my ($self) = @_;
+
+  my @objects = $self->get_all_objects;
+  my %files_by_name;
+
+  for my $obj (@objects) {
+    my $filename = join '.', grep $_, $obj->basename, $obj->extension;
+
+    my $file = $files_by_name{$filename} ||= SL::Webdav::File->new(filename => $filename, webdav => $self, loaded => 1);
+    $file->add_objects($obj);
+  }
+
+  return values %files_by_name;
+}
+
+sub get_all_objects {
+  my ($self) = @_;
+
+  my $path = $self->webdav_path;
+  my @objects;
+
+  my $base_path = $ENV{'SCRIPT_NAME'};
+  $base_path =~ s|[^/]+$||;
+  if (opendir my $dir, $path) {
+    foreach my $file (sort { lc $a cmp lc $b } map { decode("UTF-8", $_) } readdir $dir) {
+      next if (($file eq '.') || ($file eq '..'));
+
+      my $fname = $file;
+      $fname  =~ s|.*/||;
+
+      push @objects, SL::Webdav::Object->new(filename => $fname, webdav => $self);
+    }
+
+    closedir $dir;
+
+    return @objects;
+  }
+}
+
+sub get_all_latest {
+  my ($self) = @_;
+
+  my @files = $self->get_all_files;
+  map { ($_->versions)[-1] } @files;
+}
+
+sub _sanitized_number {
+  my $number = $_[0]->number;
+  $number =~ s|[/\\]|_|g;
+  $number;
+}
+
+sub webdav_path {
+  my ($self) = @_;
+
+  die "No client set in \$::auth" unless $::auth->client;
+  die "Need number"               unless $self->number;
+
+  my $type = $type_to_path{$self->type};
+
+  die "Unknown type"              unless $type;
+
+  my $path = File::Spec->catdir("webdav", $::auth->client->{id}, $type, $self->_sanitized_number);
+
+  if (!-d $path) {
+    Common::mkdir_with_parents($path);
+  }
+
+  return $path;
+}
+
+sub init_version_scheme {
+  SL::Webdav::VersionScheme::Timestamp->new;
+}
+
+1;
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Webdav - Webdav manipulation
+
+=head1 SYNOPSIS
+
+  # get list of all documents for this record
+  use SL::Webdav;
+
+  my $webdav = SL::Webdav->new(
+    type     => 'part',
+    number   => $number,
+  );
+
+  # gives you SL::Webdav::File instances
+  my $webdav_files = $webdav->get_all_files;
+
+  # gives you the objects instead
+  my $webdav_objects = $webdav->get_all_objects;
+
+  # gives you only the latest objects
+  my $webdav_objects = $webdav->get_all_latest;
+
+  # physical path to this dir
+  my $path = $webdav->webdav_path;
+
+=head1 DESCRIPTION
+
+This module is a wrapper around the webdav storage mechanism with some simple
+document management functionality.
+
+This is not a replacement for real document management, mostly because the
+underlying webdav storage ist not fully under our control. It's common practice
+to allow people direct samba access to the webdav, so all versioning
+information need to be encoded into the filename of a file, and nonsensical
+filenames must not break assumptions.
+
+This module is intended to be used if you need to scan the folder for
+previously saved files and need to build a list to display for it.
+
+If you need to manipulate the versions of a file, see L<SL::Webdav::File>
+
+If you need to access a file directly for download or metadata, see L<SL::Webdav::Object>
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<get_all_objects>
+
+Returns all L<SL::Webdav::Objects> found.
+
+=item C<get_all_files>
+
+Returns all objects sorted into L<SL::Webdav::File>s.
+
+=item C<get_all_latest>
+
+Returns only the latest object of each L<SL::Webdav::File> found.
+
+=item C<webdav_path>
+
+Returns the physical path to this webdav object.
+
+=back
+
+=head1 VERSIONING SCHEME
+
+You may register a versioning scheme object to hangdle versioning. It is
+expected to implement the following methods:
+
+=over 4
+
+=item C<separator>
+
+Must return a string that will be used to separate basename and version part of
+filenames in generating and parsing.
+
+=item C<extract_regexp>
+
+Must return a regexp that will match a versioning string at the end of a
+filename after the extension has been stripped off. It will be surrounded by
+captures.
+
+=item C<cmp>
+
+Must return a comparison function that will be invoked with two
+L<SL::Webdav::Object> instances.
+
+=item C<first_version>
+
+Must return a string representing the version of the first of a series of objects.
+
+May return undef.
+
+=item C<next_version>
+
+Will be called with the latest L<SL::Webdav::Object> and must return a new version string.
+
+=item C<keep_last_version>
+
+Will be called with the latest L<SL::Webdav::Object>. Truish return value will
+cause the latest object to be overwritten instead of creating a new version.
+
+=back
+
+=head1 BUGS AND CAVEATS
+
+=over 4
+
+=item *
+
+File operations are inconsistently L<File::Spec>ed.
+
+=back
+
+=head1 SEE ALSO
+
+L<SL::Webdav::File>, L<SL::Webdav::Object>
+
+=head1 AUTHOR
+
+Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
+
+=cut