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

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