root/branches/release-26/lib/MT/BackupRestore/BackupFileHandler.pm @ 1174

Revision 1174, 12.0 kB (checked in by bchoate, 23 months ago)

Updated copyright year for source.

  • 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                            } else {
143                                $self->{skip} += 1;
144                            }
145                            $self->{loaded} = 1;
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->count( {
259                    author_id => $obj->author_id,
260                    blog_id   => $obj->blog_id
261                });
262                $exists = 1 if $perm;
263            }
264            unless ($exists) {
265                my $result;
266                if ( $obj->id ) {
267                    $result = $obj->update();
268                }
269                else {
270                    $result = $obj->insert();
271                }
272                if ( $result ) {
273                    if ($class =~ /MT::Asset(::.+)*/) {
274                        $class = 'MT::Asset';
275                    }
276                    $self->{objects}->{"$class#$old_id"} = $obj;
277                    my $records = $self->{records};
278                    $self->{callback}->($self->{state} . " " . MT->translate("[_1] records restored...", $records), $data->{LocalName})
279                        if $records && ($records % 10 == 0);
280                    $self->{records} = $records + 1;
281                } else {
282                    push @{$self->{errors}}, $obj->errstr;
283                    $self->{callback}->($obj->errstr);
284                }
285            }
286            delete $self->{current};
287        }
288    }
289}
290
291sub end_document {
292    my $self = shift;
293    my $data = shift;
294
295    if (my $c = $self->{current_class}) {
296        my $state = $self->{state};
297        my $records = $self->{records};
298        $self->{callback}->($state . " " . MT->translate("[_1] records restored.", $records), $c->class_type || $c->datasource);
299    }
300
301    1;
302}
303
3041;
Note: See TracBrowser for help on using the browser.