Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision dc6d8231

Von Sven Schöling vor fast 10 Jahren hinzugefügt

  • ID dc6d82312f264df3d2bf482836ff4fd2bfe3a462
  • Vorgänger 49f5b7f7
  • Nachfolger b36f6606

Webdav: Framework um Dokumente im Webdav zu behandeln

Soll auf lange Sicht die Funktionen in Common ablösen.

Unterschiede anzeigen:

SL/Webdav.pm
1
package SL::Webdav;
2

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

  
6
use Encode qw(decode);
7
use File::Spec;
8
use SL::Common;
9
use SL::Webdav::File;
10
use SL::Webdav::Object;
11
use SL::Webdav::VersionScheme::Serial;
12
use SL::Webdav::VersionScheme::Timestamp;
13

  
14
use Rose::Object::MakeMethods::Generic (
15
  scalar => [ qw(type number) ],
16
  'scalar --get_set_init' => [ qw(version_scheme) ],
17
);
18

  
19
my %type_to_path = (
20
  sales_quotation         => 'angebote',
21
  sales_order             => 'bestellungen',
22
  request_quotation       => 'anfragen',
23
  purchase_order          => 'lieferantenbestellungen',
24
  sales_delivery_order    => 'verkaufslieferscheine',
25
  purchase_delivery_order => 'einkaufslieferscheine',
26
  credit_note             => 'gutschriften',
27
  invoice                 => 'rechnungen',
28
  purchase_invoice        => 'einkaufsrechnungen',
29
  part                    => 'waren',
30
  service                 => 'dienstleistungen',
31
  assembly                => 'erzeugnisse',
32
);
33

  
34
sub get_all_files {
35
  my ($self) = @_;
36

  
37
  my @objects = $self->get_all_objects;
38
  my %files_by_name;
39

  
40
  for my $obj (@objects) {
41
    my $filename = join '.', grep $_, $obj->basename, $obj->extension;
42

  
43
    my $file = $files_by_name{$filename} ||= SL::Webdav::File->new(filename => $filename, webdav => $self, loaded => 1);
44
    $file->add_objects($obj);
45
  }
46

  
47
  return values %files_by_name;
48
}
49

  
50
sub get_all_objects {
51
  my ($self) = @_;
52

  
53
  my $path = $self->webdav_path;
54
  my @objects;
55

  
56
  my $base_path = $ENV{'SCRIPT_NAME'};
57
  $base_path =~ s|[^/]+$||;
58
  if (opendir my $dir, $path) {
59
    foreach my $file (sort { lc $a cmp lc $b } map { decode("UTF-8", $_) } readdir $dir) {
60
      next if (($file eq '.') || ($file eq '..'));
61

  
62
      my $fname = $file;
63
      $fname  =~ s|.*/||;
64

  
65
      push @objects, SL::Webdav::Object->new(filename => $fname, webdav => $self);
66
    }
67

  
68
    closedir $dir;
69

  
70
    return @objects;
71
  }
72
}
73

  
74
sub get_all_latest {
75
  my ($self) = @_;
76

  
77
  my @files = $self->get_all_files;
78
  map { ($_->versions)[-1] } @files;
79
}
80

  
81
sub _sanitized_number {
82
  my $number = $_[0]->number;
83
  $number =~ s|[/\\]|_|g;
84
  $number;
85
}
86

  
87
sub webdav_path {
88
  my ($self) = @_;
89

  
90
  die "No client set in \$::auth" unless $::auth->client;
91
  die "Need number"               unless $self->number;
92

  
93
  my $type = $type_to_path{$self->type};
94

  
95
  die "Unknown type"              unless $type;
96

  
97
  my $path = File::Spec->catdir("webdav", $::auth->client->{id}, $type, $self->_sanitized_number);
98

  
99
  if (!-d $path) {
100
    Common::mkdir_with_parents($path);
101
  }
102

  
103
  return $path;
104
}
105

  
106
sub init_version_scheme {
107
  SL::Webdav::VersionScheme::Timestamp->new;
108
}
109

  
110
1;
111

  
112
__END__
113

  
114
=encoding utf-8
115

  
116
=head1 NAME
117

  
118
SL::Webdav - Webdav manipulation
119

  
120
=head1 SYNOPSIS
121

  
122
  # get list of all documents for this record
123
  use SL::Webdav;
124

  
125
  my $webdav = SL::Webdav->new(
126
    type     => 'part',
127
    number   => $number,
128
  );
129

  
130
  # gives you SL::Webdav::File instances
131
  my $webdav_files = $webdav->get_all_files;
132

  
133
  # gives you the objects instead
134
  my $webdav_objects = $webdav->get_all_objects;
135

  
136
  # gives you only the latest objects
137
  my $webdav_objects = $webdav->get_all_latest;
138

  
139
  # physical path to this dir
140
  my $path = $webdav->webdav_path;
141

  
142
=head1 DESCRIPTION
143

  
144
This module is a wrapper around the webdav storage mechanism with some simple
145
document management functionality.
146

  
147
This is not a replacement for real document management, mostly because the
148
underlying webdav storage ist not fully under our control. It's common practice
149
to allow people direct samba access to the webdav, so all versioning
150
information need to be encoded into the filename of a file, and nonsensical
151
filenames must not break assumptions.
152

  
153
This module is intended to be used if you need to scan the folder for
154
previously saved files and need to build a list to display for it.
155

  
156
If you need to manipulate the versions of a file, see L<SL::Webdav::File>
157

  
158
If you need to access a file directly for download or metadata, see L<SL::Webdav::Object>
159

  
160
=head1 FUNCTIONS
161

  
162
=over 4
163

  
164
=item C<get_all_objects>
165

  
166
Returns all L<SL::Webdav::Objects> found.
167

  
168
=item C<get_all_files>
169

  
170
Returns all objects sorted into L<SL::Webdav::File>s.
171

  
172
=item C<get_all_latest>
173

  
174
Returns only the latest object of each L<SL::Webdav::File> found.
175

  
176
=item C<webdav_path>
177

  
178
Returns the physical path to this webdav object.
179

  
180
=back
181

  
182
=head1 VERSIONING SCHEME
183

  
184
You may register a versioning scheme object to hangdle versioning. It is
185
expected to implement the following methods:
186

  
187
=over 4
188

  
189
=item C<separator>
190

  
191
Must return a string that will be used to separate basename and version part of
192
filenames in generating and parsing.
193

  
194
=item C<extract_regexp>
195

  
196
Must return a regexp that will match a versioning string at the end of a
197
filename after the extension has been stripped off. It will be surrounded by
198
captures.
199

  
200
=item C<cmp>
201

  
202
Must return a comparison function that will be invoked with two
203
L<SL::Webdav::Object> instances.
204

  
205
=item C<first_version>
206

  
207
Must return a string representing the version of the first of a series of objects.
208

  
209
May return undef.
210

  
211
=item C<next_version>
212

  
213
Will be called with the latest L<SL::Webdav::Object> and must return a new version string.
214

  
215
=item C<keep_last_version>
216

  
217
Will be called with the latest L<SL::Webdav::Object>. Truish return value will
218
cause the latest object to be overwritten instead of creating a new version.
219

  
220
=back
221

  
222
=head1 BUGS AND CAVEATS
223

  
224
=over 4
225

  
226
=item *
227

  
228
File operations are inconsistently L<File::Spec>ed.
229

  
230
=back
231

  
232
=head1 SEE ALSO
233

  
234
L<SL::Webdav::File>, L<SL::Webdav::Object>
235

  
236
=head1 AUTHOR
237

  
238
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
239

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

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

  
6
use File::Spec;
7

  
8
use Rose::Object::MakeMethods::Generic (
9
  scalar => [ qw(webdav filename loaded) ],
10
  array  => [
11
    qw(objects),
12
    add_objects => { interface => 'push', hash_key => 'objects' },
13
  ],
14
);
15

  
16
sub versions {
17
  $_[0]->load unless $_[0]->loaded;
18
  my $cmp = $_[0]->webdav->version_scheme->cmp;
19
  sort { $cmp->($a, $b) } $_[0]->objects;
20
}
21

  
22
sub latest_version {
23
  ($_[0]->versions)[-1]
24
}
25

  
26
sub load {
27
  my ($self) = @_;
28
  my @objects = $self->webdav->get_all_objects;
29
  my $ref = SL::Webdav::Object->new(filename => $self->filename, webdav => $self->webdav);
30
  my ($ref_basename, undef, $ref_extension) = $ref->parse_filename;
31

  
32
  $self->objects(grep { $_->basename eq $ref_basename && $_->extension eq $ref_extension } @objects);
33
  $self->loaded(1);
34
}
35

  
36
sub store {
37
  my ($self, %params) = @_;
38

  
39
  $self->load unless $self->loaded;
40

  
41
  my $last = $self->latest_version;
42
  my $object;
43

  
44
  if (!$last) {
45
    my $new_version  = $self->webdav->version_scheme->first_version;
46
    $object = SL::Webdav::Object->new(filename => $self->filename, webdav => $self->webdav);
47

  
48
    $self->add_objects($object);
49
  } else {
50
    if (!$self->webdav->version_scheme->keep_last_version($last)) {
51
      $params{new_version} = 1;
52
    }
53

  
54
    if ($params{new_version}) {
55
      my $new_version  = $self->webdav->version_scheme->next_version($last);
56
      my $sep          = $self->webdav->version_scheme->separator;
57
      my $new_filename = $last->basename . $sep . $new_version . "." . $last->extension;
58
      $object = SL::Webdav::Object->new(filename => $new_filename, webdav => $self->webdav);
59

  
60
      $self->add_objects($object);
61
    } else {
62
      $object = $last;
63
    }
64
  }
65

  
66
  open my $fh, '>:raw', $object->full_filedescriptor or die "could not open " . $object->filename . ": $!";
67

  
68
  $fh->print(${ $params{data} });
69

  
70
  close $fh;
71

  
72
  return $object;
73
}
74

  
75
1;
76

  
77
__END__
78

  
79
=encoding utf-8
80

  
81
=head1 NAME
82

  
83
SL::Webdav::File - Webdav file manipulation
84

  
85
=head1 SYNOPSIS
86

  
87
  use SL::Webdav::File;
88

  
89
  my $webdav_file = SL::Webdav::File->new(
90
    webdav   => $webdav,  # SL::Webdav instance
91
    filename => 'technical_drawing_AB28375.pdf',
92
  );
93

  
94
  # get existing versioned files
95
  my @webdav_objects = $webdav_file->versions;
96

  
97
  # store new version
98
  my $data = SL::Helper::CreatePDF->create_pdf(...);
99
  my $webdav_object = $webdav_file->store(data => \$data);
100

  
101
  # force new version
102
  my $webdav_object = $webdav_file->store(data => \$data, new_version => 1);
103

  
104
=head1 DESCRIPTION
105

  
106
A file in this context is the collection of all versions of a single file saved
107
into the webdav. This module provides methods to access and manipulate these
108
objects.
109

  
110
=head1 FUNCTIONS
111

  
112
=over 4
113

  
114
=item C<versions>
115

  
116
Will return all L<SL::Webdav::Object>s found in this file, sorted by version
117
according to the version scheme used.
118

  
119
=item C<latest_version>
120

  
121
Returns only the latest version object.
122

  
123
=item C<load>
124

  
125
Loads objects from disk.
126

  
127
=item C<store PARAMS>
128

  
129
Store a new version on disk. C<data> is expected to contain a reference to the
130
data to be written in raw encoding.
131

  
132
If param C<new_version> is set, force a new version, even if the versioning
133
scheme would keep the old one.
134

  
135
=back
136

  
137
=head1 SEE ALSO
138

  
139
L<SL::Webdav>, L<SL::Webdav::Object>
140

  
141
=head1 BUGS
142

  
143
None yet :)
144

  
145
=head1 AUTHOR
146

  
147
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
148

  
149
=cut
SL/Webdav/Object.pm
1
package SL::Webdav::Object;
2

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

  
6
use DateTime;
7

  
8
use Rose::Object::MakeMethods::Generic (
9
  scalar => [ qw(filename webdav) ],
10
  'scalar --get_set_init' => [ qw(version basename extension) ],
11
);
12

  
13
sub init_basename {
14
  ($_[0]->parse_filename)[0];
15
}
16

  
17
sub init_version {
18
  ($_[0]->parse_filename)[1];
19
}
20

  
21
sub init_extension {
22
  ($_[0]->parse_filename)[2];
23
}
24

  
25
sub parse_filename {
26
  my ($self) = @_;
27

  
28
  my $name = $self->filename;
29
  my $version_re = $self->webdav->version_scheme->extract_regexp;
30
  my $sep        = $self->webdav->version_scheme->separator;
31

  
32
  my $extension = $name =~ s/\.(\w+?)$//              ? $1 : '';
33
  my $version   = $name =~ s/\Q$sep\E($version_re)$// ? $1 : '';
34
  my $basename  = $name;
35

  
36
  return ($basename, $version, $extension);
37
}
38

  
39
sub full_filedescriptor {
40
  my ($self) = @_;
41

  
42
  File::Spec->catfile($self->webdav->webdav_path, $self->filename);
43
}
44

  
45
sub atime {
46
  DateTime->from_epoch(epoch => ($_[0]->stat)[8]);
47
}
48

  
49
sub mtime {
50
  DateTime->from_epoch(epoch => ($_[0]->stat)[9]);
51
}
52

  
53
sub data {
54
  my ($self) = @_;
55

  
56
  open my $fh, '<:raw', $self->full_filedescriptor or die "could not open " . $self->filename . ": $!";
57

  
58
  local $/ = undef;
59

  
60
  my $data = <$fh>;
61

  
62
  close $fh;
63

  
64
  return \$data;
65
}
66

  
67
sub stat {
68
  my $file = $_[0]->full_filedescriptor;
69
  stat($file);
70
}
71

  
72
sub href {
73
  my ($self) = @_;
74

  
75
  my $base_path = $ENV{'SCRIPT_NAME'};
76
  $base_path =~ s|[^/]+$||;
77

  
78
  my $file         = $self->filename;
79
  my $path         = $self->webdav->webdav_path;
80
  my $is_directory = -d "$path/$file";
81

  
82
  $file  = join('/', map { $::form->escape($_) } grep { $_ } split m|/+|, "$path/$file");
83
  $file .=  '/' if ($is_directory);
84

  
85
  return "$base_path/$file";
86
}
87

  
88
1;
89

  
90
__END__
91

  
92
=encoding utf-8
93

  
94
=head1 NAME
95

  
96
SL::Webdav::Object - Webdav object wrapper
97

  
98
=head1 SYNOPSIS
99

  
100
  use SL::Webdav::Object;
101

  
102
  my $object = SL::Webdav::Object->new(filename => $filename, webdav => $webdav);
103

  
104
  my $data_ref  = $object->data;
105
  my $mtime     = $object->mtime;
106

  
107
  my $basename  = $object->basename;
108
  my $version   = $object->version;
109
  my $extension = $object->extension;
110

  
111
  my $link      = $object->href;
112

  
113
=head1 DESCRIPTION
114

  
115
This is a wrapper around a single object in the webdav. These objects are
116
thought about as immutable, and all manipulation will instead happen in the
117
associated L<SL::Webdav::File>.
118

  
119
=head1 FUNCTIONS
120

  
121
=over 4
122

  
123
=item C<basename>
124

  
125
Returns the basename with version and extension stripped.
126

  
127
=item C<version>
128

  
129
Returns the version string.
130

  
131
=item C<extension>
132

  
133
Returns the extension.
134

  
135
=item C<atime>
136

  
137
L<DateTime> wrapped stat[8]
138

  
139
=item C<mtime>
140

  
141
L<DateTime> wrapped stat[9]
142

  
143
=item C<data>
144

  
145
Ref to the actual data in raw encoding.
146

  
147
=item C<href>
148

  
149
URL relative to the web base dir for download.
150

  
151
=back
152

  
153
=head1 SEE ALSO
154

  
155
L<SL::Webdav>, L<SL::Webdav::File>
156

  
157
=head1 BUGS
158

  
159
None yet :)
160

  
161
=head1 AUTHOR
162

  
163
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
164

  
165
=cut
SL/Webdav/VersionScheme/Serial.pm
1
package SL::Webdav::VersionScheme::Serial;
2

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

  
6
use DateTime;
7

  
8
sub separator { "-" }
9

  
10
sub extract_regexp { qr/\d+/ }
11

  
12
sub cmp { sub { $_[0]->version <=> $_[1]->version } }
13

  
14
sub first_version { }
15

  
16
sub next_version { $_[1]->version + 1 }
17

  
18
sub keep_last_version {
19
  my ($self, $last) = @_;
20

  
21
  if ($::lxoffice_conf->{webdav}{new_version_after_minutes}) {
22
    return DateTime->now <= $last->mtime + DateTime::Duration->new(minutes => $::lx_office_conf{webdav}{new_version_after_minutes});
23
  } else {
24
    return 0;
25
  }
26
}
27

  
28
1;
SL/Webdav/VersionScheme/Timestamp.pm
1
package SL::Webdav::VersionScheme::Timestamp;
2

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

  
6
use POSIX;
7

  
8
sub separator { "_" }
9

  
10
sub extract_regexp { qr/\d{8}_\d{6}/ }
11

  
12
sub cmp { sub { $_[0]->version cmp $_[1]->version } }
13

  
14
sub first_version { goto &get_current_formatted_time }
15

  
16
sub next_version { goto &get_current_formatted_time }
17

  
18
sub keep_last_version {
19
  0;
20
}
21

  
22
sub get_current_formatted_time {
23
  return POSIX::strftime('%Y%m%d_%H%M%S', localtime());
24
}
25

  
26
1;

Auch abrufbar als: Unified diff