root/mt-db-convert/trunk/mt-db-convert31.cgi

Revision 230, 13.5 kB (checked in by ogawa, 2 years ago)

Rename mt-db-convert.cgi to mt-db-convert32.cgi.
Add mt-db-convert33.cgi for Movable Type 3.3x.
Add more strict version checking.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Rev
Line 
1#!/usr/bin/perl -w
2# mt-db-convert.cgi: converting your MT data between multiple db engines
3# This is a derived work from the following:
4
5# Copyright 2001-2005 Six Apart. This code cannot be redistributed without
6# permission from www.movabletype.org.
7#
8# $Id: mt-db2sql.cgi 12446 2005-05-25 21:32:39Z bchoate $
9use strict;
10
11my($MT_DIR);
12BEGIN {
13    if ($0 =~ m!(.*[/\\])!) {
14        $MT_DIR = $1;
15    } else {
16        $MT_DIR = './';
17    }
18    unshift @INC, $MT_DIR . 'lib';
19    unshift @INC, $MT_DIR . 'extlib';
20}
21
22local $| = 1;
23print "Content-Type: text/html\n\n";
24print show_header();
25
26my @CLASSES = qw( MT::Author MT::Blog MT::Trackback MT::Category MT::Comment MT::Entry
27                  MT::IPBanList MT::Log MT::Notification MT::Permission
28                  MT::Placement MT::Template MT::TemplateMap
29                  MT::TBPing );
30
31use File::Spec;
32
33my @DBSPECS = qw( DataSource ObjectDriver Database DBUser DBHost DBPassword );
34my ($src_cfg, $dst_cfg);
35
36eval {
37    local $SIG{__WARN__} = sub { print "**** WARNING: $_[0]\n" };
38
39    require MT;
40    my $mt = MT->new( Config => $MT_DIR . 'mt.cfg', Directory => $MT_DIR )
41        or die MT->errstr;
42    die "This script is for Movable Type 3.1 family."
43        unless $mt->version_number >= 3.1 && $mt->version_number < 3.2;
44
45    my $cfg = $mt->{cfg};
46
47    require CGI;
48    my $q = CGI->new;
49    foreach (@DBSPECS) {
50        $src_cfg->{$_} = $q->param('src_' . $_) || '';
51        $dst_cfg->{$_} = $q->param('dst_' . $_) || '';
52    }
53
54    # if src and dst dbspecs not given
55    if (!$src_cfg->{ObjectDriver} ||
56        ($src_cfg->{ObjectDriver} eq 'DBM' && !$src_cfg->{DataSource}) ||
57        !$dst_cfg->{ObjectDriver} ||
58        ($dst_cfg->{ObjectDriver} eq 'DBM' && !$dst_cfg->{DataSource})) {
59        $src_cfg->{$_} ||= $cfg->$_() || '' foreach (@DBSPECS);
60        print show_form($q->url || 'mt-db-convert.cgi', $src_cfg, $dst_cfg);
61        print show_footer();
62        exit;
63    }
64
65    print "<pre>\n\n";
66    require MT::Object;
67    my $type = ($dst_cfg->{ObjectDriver} =~ /^DBI::(.*)$/) ? $1 : '';
68    if ($type) {
69    # set dst driver
70    $cfg->set($_, $dst_cfg->{$_}) foreach (@DBSPECS);
71    MT::Object->set_driver($dst_cfg->{ObjectDriver})
72        or die MT::ObjectDriver->errstr;
73    my $dbh = MT::Object->driver->{dbh};
74    my $schema = File::Spec->catfile($MT_DIR, 'schemas', $type . '.dump');
75    open FH, $schema or die "Can't open schema file '$schema': $!";
76    my $ddl;
77    { local $/; $ddl = <FH> }
78    close FH;
79    my @stmts = split /;/, $ddl;
80    print "Loading database schema...\n\n";
81    for my $stmt (@stmts) {
82        $stmt =~ s!^\s*!!;
83        $stmt =~ s!\s*$!!;
84        next unless $stmt =~ /\S/;
85        $dbh->do($stmt) or die $dbh->errstr;
86    }
87    }
88
89    ## %ids will hold the highest IDs of each class.
90    my %ids;
91
92    print "Loading data...\n";
93    for my $class (@CLASSES) {
94        print $class, "\n";
95        # set src driver
96        $cfg->set($_, $src_cfg->{$_}) foreach (@DBSPECS);
97        MT::Object->set_driver($src_cfg->{ObjectDriver});
98        eval "use $class";
99        my $iter = $class->load_iter;
100
101        my %names;
102        my %cat_parent;
103        my $i = 0;
104
105        # set dst driver
106        $cfg->set($_, $dst_cfg->{$_}) foreach (@DBSPECS);
107        MT::Object->set_driver($dst_cfg->{ObjectDriver});
108        MT::Object->driver->{dbh}->begin_work if $type eq 'sqlite';
109        while (my $obj = $iter->()) {
110            $ids{$class} = $obj->id
111                if !$ids{$class} || $obj->id > $ids{$class};
112            ## Look for duplicate template, category, and author names,
113            ## because we have uniqueness constraints in the DB.
114            if ($class eq 'MT::Template') {
115                my $key = lc($obj->name) . $obj->blog_id;
116                if ($names{$class}{$key}++) {
117                    print "        Found duplicate template name '" .
118                          $obj->name;
119                    $obj->name($obj->name . ' ' . $names{$class}{$key});
120                    print "'; renaming to '" . $obj->name . "'\n";
121                }
122                ## Touch the text column to make sure we read in
123                ## any linked templates.
124                my $text = $obj->text;
125            } elsif ($class eq 'MT::Author') {
126                my $key = lc($obj->name);
127                if ($names{$class . $obj->type}{$key}++) {
128                    print "        Found duplicate author name '" .
129                          $obj->name;
130                    $obj->name($obj->name . ' ' . $names{$class}{$key});
131                    print "'; renaming to '" . $obj->name . "'\n";
132                }
133                $obj->email('') unless defined $obj->email;
134                $obj->set_password('') unless defined $obj->password;
135            } elsif ($class eq 'MT::Category') {
136                my $key = lc($obj->label) . $obj->blog_id;
137                if ($names{$class}{$key}++) {
138                    print "        Found duplicate category label '" .
139                          $obj->label;
140                    $obj->label($obj->label . ' ' . $names{$class}{$key});
141                    print "'; renaming to '" . $obj->label . "'\n";
142                }
143                # save the parent value for assignment at the end
144                if ($obj->parent) {
145                    $cat_parent{$obj->id} = $obj->parent;
146                    $obj->parent(0);
147                }
148            } elsif ($class eq 'MT::Trackback') {
149                $obj->entry_id(0) unless defined $obj->entry_id;
150                $obj->category_id(0) unless defined $obj->category_id;
151            } elsif ($class eq 'MT::Entry') {
152                $obj->allow_pings(0)
153                    if defined $obj->allow_pings && $obj->allow_pings eq '';
154                $obj->allow_comments(0)
155                    if defined $obj->allow_comments && $obj->allow_comments eq '';
156            } elsif ($class eq 'MT::Blog') {
157                $obj->touch(); # for updating children_modified_on field
158            }
159
160            $obj->save
161                or die $obj->errstr;
162            $i++;
163            print '.' . (($i % 10) ? '' : ' ') . (($i % 100) ? '' : "\n");
164        }
165
166        # fix up the category parents
167        foreach my $id (keys %cat_parent) {
168            my $cat = MT::Category->load($id);
169            $cat->parent( $cat_parent{$id} );
170            $cat->save;
171        }
172
173        print "\n($i objects saved.)\n\n";
174        MT::Object->driver->{dbh}->commit if $type eq 'sqlite';
175    }
176
177    if ($type eq 'postgres') {
178        print "Updating sequences\n";
179        my $dbh = MT::Object->driver->{dbh};
180        for my $class (keys %ids) {
181            print "    $class => $ids{$class}\n";
182            my $seq = 'mt_' . $class->datasource . '_' .
183                      $class->properties->{primary_key};
184            $dbh->do("select setval('$seq', $ids{$class})")
185                or die $dbh->errstr;
186        }
187    }
188};
189if ($@) {
190    print <<HTML;
191
192An error occurred while loading data:
193
194$@
195
196HTML
197} else {
198    print <<HTML;
199
200Done copying data from $src_cfg->{ObjectDriver} to $dst_cfg->{ObjectDriver}! All went well.
201
202HTML
203    print "Your recommended setting\n-------------------------------------\n";
204    foreach (@DBSPECS) {
205        next unless $src_cfg->{$_};
206        if (($src_cfg->{ObjectDriver} eq 'DBM' && $_ ne 'ObjectDriver') ||
207            ($src_cfg->{ObjectDriver} ne 'DBM' && $_ ne 'DataSource')) {
208            print "# $_ $src_cfg->{$_}\n";
209        }
210    }
211    foreach (@DBSPECS) {
212        next unless $dst_cfg->{$_};
213        if (($dst_cfg->{ObjectDriver} eq 'DBM' && $_ ne 'ObjectDriver') ||
214            ($dst_cfg->{ObjectDriver} ne 'DBM' && $_ ne 'DataSource')) {
215            print "$_ $dst_cfg->{$_}\n";
216        }
217    }
218    print "-------------------------------------\n";
219}
220
221print "</pre>\n";
222print show_footer();
223
224sub show_header {
225    my $html = <<'HTML';
226<html>
227<head>
228  <title>mt-db-convert.cgi: Converting your MT data between DB engines</title>
229  <style type="text/css">
230  body { font-family: "trebuchet ms", arial, sans-serif; font-size: 90%; }
231    h1 { font-size: 100%; }
232    fieldset { width: 40%; float: left; background: #EEE;}
233  </style>
234</head>
235<body>
236<h1>mt-db-convert.cgi($Rev$): Converting your MT data between DB engines (for MT 3.1)</h1>
237HTML
238}
239
240sub show_form {
241    my ($name, $src_cfg, $dst_cfg) = @_;
242    my (%src_sel, %dst_sel);
243    for (qw( DBM DBI::mysql DBI::postgres DBI::sqlite )) {
244        $src_sel{$_} = ($_ eq $src_cfg->{ObjectDriver}) ? 'selected' : '';
245        $dst_sel{$_} = ($_ eq $dst_cfg->{ObjectDriver}) ? 'selected' : '';
246    }
247    my $html = <<HTML;
248<p>Please fill the following:</p>
249
250<form id="dbconvert" method="post" action="$name">
251  <fieldset>
252    <legend>Source DB Configuration</legend>
253    <p>
254    <label>ObjectDriver:</label><br />
255    <select name="src_ObjectDriver" onchange="refresh_src()">
256      <option value="">Select your source driver</option>
257      <option value="DBM" $src_sel{'DBM'}>BerkeleyDB</option>
258      <option value="DBI::mysql" $src_sel{'DBI::mysql'}>MySQL</option>
259      <option value="DBI::postgres" $src_sel{'DBI::postgres'}>PostgreSQL</option>
260      <option value="DBI::sqlite" $src_sel{'DBI::sqlite'}>SQLite</option>
261    </select>
262    </p>
263
264    <p>
265    <label>DataSource:</label><br />
266    <input name="src_DataSource" type="text" value="$src_cfg->{DataSource}" size="50" /><br />
267    <small>BerkeleyDB requires the full path to your database directory (e.g., ${MT_DIR}db).</small>
268    </p>
269
270    <p>
271    <label>Database:</label><br />
272    <input name="src_Database" type="text" value="$src_cfg->{Database}" size="50" /><br />
273    <small>SQLite requires the full path to your SQLite database file (e.g., ${MT_DIR}db/sqlite.db).<br />MySQL and PostgreSQL require the database name.</small>
274    </p>
275
276    <p>
277    <label>DBUser:</label><br />
278    <input name="src_DBUser" type="text" value="$src_cfg->{DBUser}" size="50" /><br />
279    <small>MySQL and PostgreSQL require the database user name.</small>
280    </p>
281
282    <p>
283    <label>DBPassword:</label><br />
284    <input name="src_DBPassword" type="password" value="" size="50" /><br />
285    <small>MySQL and PostgreSQL require the database user password.</small>
286    </p>
287
288    <p>
289    <label>DBHost:</label><br />
290    <input name="src_DBHost" type="text" value="$src_cfg->{DBHost}" size="50" /><br />
291    <small>MySQL and PostgreSQL require the database host name.</small>
292    </p>
293  </fieldset>
294
295  <fieldset>
296    <legend>Destination DB Configuration</legend>
297    <p>
298    <label>ObjectDriver:</label><br />
299    <select name="dst_ObjectDriver" onchange="refresh_dst()">
300      <option value="">Select your destination driver</option>
301      <option value="DBM" $dst_sel{'DBM'}>BerkeleyDB</option>
302      <option value="DBI::mysql" $dst_sel{'DBI::mysql'}>MySQL</option>
303      <option value="DBI::postgres" $dst_sel{'DBI::postgres'}>PostgreSQL</option>
304      <option value="DBI::sqlite" $dst_sel{'DBI::sqlite'}>SQLite</option>
305    </select>
306    </p>
307
308    <p>
309    <label>DataSource:<label><br />
310    <input name="dst_DataSource" type="text" value="$dst_cfg->{DataSource}" size="50" /><br />
311    <small>BerkeleyDB requires the full path to your database directory (e.g., ${MT_DIR}db).</small>
312    </p>
313
314    <p>
315    <label>Database:</label><br />
316    <input name="dst_Database" type="text" value="$dst_cfg->{Database}" size="50" /><br />
317    <small>SQLite requires the full path to your SQLite database file (e.g., ${MT_DIR}db/sqlite.db).<br />MySQL and PostgreSQL require the database name.</small>
318    </p>
319
320    <p>
321    <label>DBUser:</label><br />
322    <input name="dst_DBUser" type="text" value="$dst_cfg->{DBUser}" size="50" /><br />
323    <small>MySQL and PostgreSQL require the database user name.</small>
324    </p>
325
326    <p>
327    <label>DBPassword:</label><br />
328    <input name="dst_DBPassword" type="password" value="" size="50" /><br />
329    <small>MySQL and PostgreSQL require the database user password.</small>
330    </p>
331
332    <p>
333    <label>DBHost:</label><br />
334    <input name="dst_DBHost" type="text" value="$dst_cfg->{DBHost}" size="50" /><br />
335    <small>MySQL and PostgreSQL require the database host name.</small>
336    </p>
337  </fieldset>
338
339  <p style="clear: both;"><input type="submit" value="Convert" /></p>
340</form>
341<script type="text/javascript">
342var f = document.forms['dbconvert'];
343function refresh_src() {
344  f.src_DataSource.disabled = 'disabled';
345  f.src_Database.disabled = 'disabled';
346  f.src_DBUser.disabled = 'disabled';
347  f.src_DBHost.disabled = 'disabled';
348  f.src_DBPassword.disabled = 'disabled';
349  if (f.src_ObjectDriver.value == 'DBM') {
350    f.src_DataSource.disabled = '';
351  } else if (f.src_ObjectDriver.value == 'DBI::mysql' || f.src_ObjectDriver.value == 'DBI::postgres') {
352    f.src_Database.disabled = '';
353    f.src_DBUser.disabled = '';
354    f.src_DBHost.disabled = '';
355    f.src_DBPassword.disabled = '';
356  } else if (f.src_ObjectDriver.value == 'DBI::sqlite') {
357    f.src_Database.disabled = '';
358  }
359}
360function refresh_dst() {
361  f.dst_DataSource.disabled = 'disabled';
362  f.dst_Database.disabled = 'disabled';
363  f.dst_DBUser.disabled = 'disabled';
364  f.dst_DBHost.disabled = 'disabled';
365  f.dst_DBPassword.disabled = 'disabled';
366  if (f.dst_ObjectDriver.value == 'DBM') {
367    f.dst_DataSource.disabled = '';
368  } else if (f.dst_ObjectDriver.value == 'DBI::mysql' || f.dst_ObjectDriver.value == 'DBI::postgres') {
369    f.dst_Database.disabled = '';
370    f.dst_DBUser.disabled = '';
371    f.dst_DBHost.disabled = '';
372    f.dst_DBPassword.disabled = '';
373  } else if (f.dst_ObjectDriver.value == 'DBI::sqlite') {
374    f.dst_Database.disabled = '';
375  }
376}
377refresh_src();
378refresh_dst();
379</script>
380HTML
381}
382
383sub show_footer {
384    my $html = <<'HTML';
385<hr />
386<address>Hirotaka Ogawa (<a href="http://as-is.net/blog/">http://as-is.net/blog/</a>)</address>
387</body>
388</html>
389HTML
390}
Note: See TracBrowser for help on using the browser.