1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2017 Best Practical Solutions, LLC
6 # <sales@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
49 package RT::Migrate::Importer::File;
53 use base qw(RT::Migrate::Importer);
63 # Directory is required
64 die "Directory is required" unless $args{Directory};
65 die "Invalid path $args{Directory}" unless -d $args{Directory};
66 $self->{Directory} = $args{Directory};
68 # Load metadata, if present
69 if (-e "$args{Directory}/rt-serialized") {
70 my $dat = eval { Storable::retrieve("$args{Directory}/rt-serialized"); }
71 or die "Failed to load metadata" . ($@ ? ": $@" : "");
72 $self->LoadMetadata($dat);
76 $self->{Statefile} = $args{Statefile} || "$args{Directory}/partial-import";
77 unlink $self->{Statefile}
78 if -f $self->{Statefile} and not $args{Resume};
80 return $self->SUPER::Init(@_);
85 my $dir = $self->{Directory};
87 if ($self->{Metadata} and $self->{Metadata}{Files}) {
88 $self->{Files} = [ map {s|^.*/|$dir/|;$_} @{$self->{Metadata}{Files}} ];
90 $self->{Files} = [ <$dir/*.dat> ];
92 $self->{Files} = [ map {File::Spec->rel2abs($_)} @{ $self->{Files} } ];
94 $self->RestoreState( $self->{Statefile} );
96 local $SIG{ INT } = sub { $self->{INT} = 1 };
97 local $SIG{__DIE__} = sub { warn "\n", @_; $self->SaveState; exit 1 };
99 $self->{Progress}->(undef) if $self->{Progress};
100 while (@{$self->{Files}}) {
101 $self->{Filename} = shift @{$self->{Files}};
102 open(my $fh, "<", $self->{Filename})
103 or die "Can't read $self->{Filename}: $!";
105 seek($fh, $self->{Seek}, 0)
106 or die "Can't seek to $self->{Seek} in $self->{Filename}";
107 $self->{Seek} = undef;
109 while (not eof($fh)) {
110 $self->{Position} = tell($fh);
112 # Stop when we're at a good stopping point
113 die "Caught interrupt, quitting.\n" if $self->{INT};
115 $self->ReadStream( $fh );
121 # Return creation counts
122 return $self->ObjectCount;
127 my $dir = $self->{Directory};
129 my %found = ( "RT::System" => 1 );
130 my @files = ($self->{Metadata} and $self->{Metadata}{Files}) ?
131 @{ $self->{Metadata}{Files} } : <$dir/*.dat>;
132 @files = map {File::Spec->rel2abs($_)} @files;
134 for my $filename (@files) {
135 open(my $fh, "<", $filename)
136 or die "Can't read $filename: $!";
137 while (not eof($fh)) {
138 my $loaded = Storable::fd_retrieve($fh);
139 if (ref $loaded eq "HASH") {
140 $self->LoadMetadata( $loaded );
144 if ($self->{DumpObjects}) {
145 print STDERR Data::Dumper::Dumper($loaded), "\n"
146 if $self->{DumpObjects}{ $loaded->[0] };
149 my ($class, $uid, $data) = @{$loaded};
150 $self->{ObjectCount}{$class}++;
152 delete $self->{Pending}{$uid};
153 for (grep {ref $data->{$_}} keys %{$data}) {
154 my $uid_ref = ${ $data->{$_} };
155 unless (defined $uid_ref) {
156 push @{ $self->{Invalid} }, { uid => $uid, column => $_ };
159 next if $found{$uid_ref};
160 next if $uid_ref =~ /^RT::Principal-/;
161 push @{$self->{Pending}{$uid_ref} ||= []}, {uid => $uid};
166 return $self->ObjectCount;
171 my ($statefile) = @_;
172 return unless $statefile && -f $statefile;
174 my $state = Storable::retrieve( $self->{Statefile} );
175 $self->{$_} = $state->{$_} for keys %{$state};
176 unlink $self->{Statefile};
178 print STDERR "Resuming partial import...\n";
187 unshift @{$self->{Files}}, $self->{Filename};
188 $self->{Seek} = $self->{Position};
189 $data{$_} = $self->{$_} for
190 qw/Filename Seek Position Files
191 Organization ObjectCount
193 SkipTransactions Pending Invalid
195 OriginalId ExcludeOrganization Clone
197 Storable::nstore(\%data, $self->{Statefile});
201 Importer state has been written to the file:
204 It may be possible to resume the import by re-running rt-importer.