root/branches/release-38/lib/MT/BackupRestore/BackupFileHandler.pm @ 2298

Revision 2298, 15.0 kB (checked in by fumiakiy, 19 months ago)

Mark the object loaded so it won't be inserted. BugId:79678

  • Property svn:keywords set to Id Author Date Revision
Line 
1# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id$
6
7package MT::BackupRestore::BackupFileHandler;
8
9use strict;
10use XML::SAX::Base;
11use MIME::Base64;
12
13@MT::BackupRestore::BackupFileHandler::ISA = qw(XML::SAX::Base);
14
15sub new {
16    my $class = shift;
17    my (%param) = @_;
18    my $self = bless \%param, $class;
19    return $self;
20}
21
22sub start_document {
23    my $self = shift;
24    my $data = shift;
25
26    $self->{start} = 1;
27
28    1;
29}
30
31sub start_element {
32    my $self = shift;
33    my $data = shift;
34
35    return if $self->{skip};
36
37    my $name = $data->{LocalName};
38    my $attrs = $data->{Attributes};
39    my $ns = $data->{NamespaceURI};
40
41    if ($self->{start}) {
42        die MT->translate('Uploaded file was not a valid Movable Type backup manifest file.')
43            if !(('movabletype' eq $name) && (MT::BackupRestore::NS_MOVABLETYPE() eq $ns));
44        unless ($self->{ignore_schema_conflicts}) {
45            my $schema = $attrs->{'{}schema_version'}->{Value};
46            if (('ignore' ne $self->{schema_version}) && ($schema > $self->{schema_version})) {
47                $self->{critical} = 1;
48                my $message = MT->translate('Uploaded file was backed up from Movable Type with the newer schema version ([_1]) than the one in this system ([_2]).  It is not safe to restore the file to this version of Movable Type.', MT::I18N::encode_text(MT::I18N::utf8_off($schema), 'utf-8'), $self->{schema_version});
49                MT->log({ 
50                    message => $message,
51                    level => MT::Log::ERROR(),
52                    class => 'system',
53                    category => 'restore',
54                });
55                die $message;
56            }
57        }
58        $self->{start} = 0;
59        return 1;
60    }
61
62    my $objects = $self->{objects};
63    my $deferred = $self->{deferred};
64    my $callback = $self->{callback};
65
66    if (my $current = $self->{current}) {
67        # this is an element for a text column of the object
68        $self->{current_text} = [ $name ];
69    } else {
70        if (MT::BackupRestore::NS_MOVABLETYPE() eq $ns) {
71            my $class = MT->model($name);
72            unless ($class) {
73                push @{$self->{errors}}, MT->translate('[_1] is not a subject to be restored by Movable Type.', $name);
74            } else {
75                if ($self->{current_class} ne $class) {
76                    if (my $c = $self->{current_class}) {
77                        my $state = $self->{state};
78                        my $records = $self->{records};
79                        $callback->($state . " " . MT->translate("[_1] records restored.", $records), $c->class_type || $c->datasource);
80                    }
81                    $self->{records} = 0;
82                    $self->{current_class} = $class;
83                    my $state = MT->translate('Restoring [_1] records:', $class);
84                    $callback->($state, $name);
85                    $self->{state} = $state;
86                }
87                my %column_data = map { $attrs->{$_}->{LocalName} => 
88                        MT::I18N::encode_text(MT::I18N::utf8_off($attrs->{$_}->{Value}), 'utf-8')
89                    } keys(%$attrs);
90                my $obj;
91                if ( 'author' eq $name ) {
92                    $obj = $class->load({ name => $column_data{name} });
93                    if ($obj) {
94                        if ( UNIVERSAL::isa( MT->instance, 'MT::App' )
95                          && ( $obj->id == MT->instance->user->id ) ) {
96                            MT->log({ message => MT->translate(
97                                "User with the same name as the name of the currently logged in ([_1]) found.  Skipped the record.", $obj->name),
98                                level => MT::Log::INFO(),
99                                metadata => 'Permissions and Associations have been restored.',
100                                class => 'system',
101                                category => 'restore',
102                            });
103                            $objects->{"$class#" . $column_data{id}} = $obj;
104                            $self->{current} = $obj;
105                            $self->{loaded} = 1;
106                            $self->{skip} += 1;
107                        }
108                        else {
109                            $self->{callback}->("\n");
110                            MT->log({ message => MT->translate(
111                                "User with the same name '[_1]' found (ID:[_2]).  Restore replaced this user with the data backed up.",
112                                                  $obj->name, $obj->id),
113                                level => MT::Log::INFO(),
114                                metadata => 'Permissions and Associations have been restored as well.',
115                                class => 'system',
116                                category => 'restore',
117                            });
118                            my $old_id = delete $column_data{id};
119                            $objects->{"$class#$old_id"} = $obj;
120                            my $child_classes = $obj->properties->{child_classes} || {};
121                            for my $class (keys %$child_classes) {
122                                eval "use $class;";
123                                $class->remove({ author_id => $obj->id, blog_id => '0' });
124                            }
125                            my $success = $obj->restore_parent_ids(\%column_data, $objects);
126                            if ($success) {
127                                my %realcolumns = map { $_ => delete($column_data{$_}) }
128                                    @{ $obj->column_names };
129                                $obj->set_values(\%realcolumns);
130                                $obj->$_($column_data{$_}) foreach keys( %column_data );
131                                $self->{current} = $obj;
132                            } else {
133                                $deferred->{$class . '#' . $column_data{id}} = 1;
134                                $self->{deferred} = $deferred;
135                                $self->{skip} += 1;
136                            }
137                            $self->{loaded} = 1;
138                        }
139                    }
140                } elsif ('template' eq $name) {
141                    if (!$column_data{blog_id}) {
142                        $obj = $class->load({ blog_id => 0, identifier => $column_data{identifier} });
143                        if ($obj) {
144                            my $old_id = delete $column_data{id};
145                            $objects->{"$class#$old_id"} = $obj;
146                            if ($self->{overwrite_template}) {
147                                my %realcolumns = map { $_ => delete($column_data{$_}) }
148                                    @{ $obj->column_names };
149                                $obj->set_values(\%realcolumns);
150                                $obj->$_($column_data{$_}) foreach keys( %column_data );
151                                $self->{current} = $obj;
152                                $self->{loaded} = 1;
153                            } else {
154                                $self->{skip} += 1;
155                            }
156                        }
157                    }
158                }
159                unless ($obj) {
160                    $obj = $class->new;
161                }
162                unless ($obj->id) {
163                    my $success = $obj->restore_parent_ids(\%column_data, $objects);
164                    if ($success) {
165                        require MT::Meta;
166                        my @metacolumns = MT::Meta->metadata_by_class( ref($obj) );
167                        my %metacolumns = map { $_->{name} => $_->{type} } @metacolumns;
168                        $self->{metacolumns}{ref($obj)} = \%metacolumns;
169                        my %realcolumn_data = map { $_ => $column_data{$_} }
170                            grep { !exists($metacolumns{$_}) }
171                                keys %column_data;
172                        $obj->set_values(\%realcolumn_data);
173                        foreach my $metacol ( keys %metacolumns ) {
174                            next if ( 'vclob' eq $metacolumns{$metacol} )
175                                 || ( 'vblob' eq $metacolumns{$metacol} );
176                            $obj->$metacol( $column_data{$metacol} );
177                        }
178                        $self->{current} = $obj;
179                    } else {
180                        $deferred->{$class . '#' . $column_data{id}} = 1;
181                        $self->{deferred} = $deferred;
182                        $self->{skip} += 1;
183                    }
184                }
185            }
186        } else {
187            my $obj = MT->run_callbacks("Restore.$name:$ns", $data, $objects, $deferred, $callback);
188            $self->{current} = $obj if defined($obj) && ('1' ne $obj);
189        }
190    }
191    1;
192}
193
194sub characters {
195    my $self = shift;
196    my $data = shift;
197
198    return if $self->{skip};
199    return if !exists($self->{current});
200    if (my $text_data = $self->{current_text}) {
201        push @$text_data, MT::I18N::utf8_off($data->{Data});
202        $self->{current_text} = $text_data;
203    }
204    1;
205}
206
207sub end_element {
208    my $self = shift;
209    my $data = shift;
210
211    if ($self->{skip}) {
212        $self->{skip} -= 1;
213        return;
214    }
215
216    my $name  = $data->{LocalName};
217    my $ns    = $data->{NamespaceURI};
218    my $class = MT->model($name);
219
220    if (my $obj = $self->{current}) {
221        if (my $text_data = delete $self->{current_text}) {
222            my $column_name = shift @$text_data;
223            my $text;
224            $text .= $_ foreach @$text_data;
225           
226            my $defs = $obj->column_defs;
227            if ( exists( $defs->{$column_name} ) ) {
228                if ('blob' eq $defs->{$column_name}->{type}) {
229                    $obj->column($column_name, MIME::Base64::decode_base64($text));
230                } else {
231                    $text = MT::I18N::encode_text($text, 'utf-8');
232                    $obj->column($column_name, $text);
233                }
234            }
235            elsif ( my $metacolumns = $self->{metacolumns}{ref($obj)} ) {
236                if ( my $type = $metacolumns->{$column_name} ) {
237                    if ( 'vblob' eq $type ) {
238                        $text = MIME::Base64::decode_base64($text);
239                        $text = MT::Serialize->unserialize( $text );
240                        $obj->$column_name( $$text );
241                    }
242                    else {
243                        $text = MT::I18N::encode_text($text, 'utf-8');
244                        $obj->$column_name( $text );
245                    }
246                }
247            }
248        } else {
249            my $old_id = $obj->id;
250            unless ((('author' eq $name) || ('template' eq $name)) && (exists $self->{loaded})) {
251                delete $obj->{column_values}->{id};
252                delete $obj->{changed_cols}->{id};
253            } else {
254                delete $self->{loaded};
255            }
256            my $exists = 0;
257            if ('tag' eq $name) {
258                if (my $tag = MT::Tag->load({ name => $obj->name }, { binary => { name => 1 } } )) {
259                    $exists = 1;
260                    $self->{objects}->{"$class#$old_id"} = $tag;
261                    $self->{callback}->("\n");
262                    $self->{callback}->(
263                        MT->translate("Tag '[_1]' exists in the system.",
264                            $obj->name)
265                    );
266                }
267            } elsif ('trackback' eq $name) {
268                my $term;
269                my $message;
270                if ($obj->entry_id) {
271                    $term = { entry_id => $obj->entry_id };
272                } elsif ($obj->category_id) {
273                    $term = { category_id => $obj->category_id };
274                }
275                if (my $tb = $class->load($term)) {
276                    $exists = 1;
277                    my $changed = 0;
278                    if ($obj->passphrase) {
279                        $tb->passphrase($obj->passphrase);
280                        $changed = 1;
281                    }
282                    if ($obj->is_disabled) {
283                        $tb->is_disabled($obj->is_disabled);
284                        $changed = 1;
285                    }
286                    $tb->save if $changed;
287                    $self->{objects}->{"$class#$old_id"} = $tb;
288                    my $records = $self->{records};
289                    $self->{callback}->($self->{state} . " " . MT->translate("[_1] records restored...", $records), $data->{LocalName})
290                        if $records && ($records % 10 == 0);
291                    $self->{records} = $records + 1;
292                }
293            }
294            elsif ('permission' eq $name) {
295                my $perm = $class->exist( {
296                    author_id => $obj->author_id,
297                    blog_id   => $obj->blog_id
298                });
299                $exists = 1 if $perm;
300            }
301            elsif ('objectscore' eq $name) {
302                my $score = $class->exist( {
303                    author_id => $obj->author_id,
304                    object_id => $obj->object_id,
305                    object_ds => $obj->object_ds,
306                });
307                $exists = 1 if $score;
308            }
309            elsif ('field' eq $name) {
310                # Available in propack only
311                if ( $obj->blog_id == 0 ) {
312                    my $field = $class->exist( {
313                        blog_id => 0,
314                        basename => $obj->basename,
315                    } );
316                    $exists = 1 if $field;
317                }
318            }
319            unless ($exists) {
320                my $result;
321                if ( $obj->id ) {
322                    $result = $obj->update();
323                }
324                else {
325                    $result = $obj->insert();
326                }
327                if ( $result ) {
328                    if ($class =~ /MT::Asset(::.+)*/) {
329                        $class = 'MT::Asset';
330                    }
331                    $self->{objects}->{"$class#$old_id"} = $obj;
332                    my $records = $self->{records};
333                    $self->{callback}->($self->{state} . " " . MT->translate("[_1] records restored...", $records), $data->{LocalName})
334                        if $records && ($records % 10 == 0);
335                    $self->{records} = $records + 1;
336                    my $cb = "restored.$name";
337                    $cb .= ":$ns" if MT::BackupRestore::NS_MOVABLETYPE() ne $ns;
338                    MT->run_callbacks( $cb, $obj, $self->{callback} );
339                    $obj->call_trigger('post_save', $obj);
340                } else {
341                    push @{$self->{errors}}, $obj->errstr;
342                    $self->{callback}->($obj->errstr);
343                }
344            }
345            delete $self->{current};
346        }
347    }
348}
349
350sub end_document {
351    my $self = shift;
352    my $data = shift;
353
354    if (my $c = $self->{current_class}) {
355        my $state = $self->{state};
356        my $records = $self->{records};
357        $self->{callback}->($state . " " . MT->translate("[_1] records restored.", $records), $c->class_type || $c->datasource);
358    }
359
360    1;
361}
362
3631;
Note: See TracBrowser for help on using the browser.