root/branches/release-35/lib/MT/BackupRestore/BackupFileHandler.pm @ 1905

Revision 1905, 12.3 kB (checked in by fumiakiy, 20 months ago)

Do not restore duplicated scoring for an author. This may happen when an author scores another author and both exist in the database while restoring. BugId:75914

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