00001 <?php
00002
00003 namespace Habari;
00004
00005 if ( !defined( 'HABARI_PATH' ) ) { die( 'No direct access' ); }
00006
00007
00008 if ( !defined('IMPORT_BATCH') ) {
00009 define('IMPORT_BATCH', 100);
00010 }
00011
00022 class WPImport extends Plugin implements Importer {
00023
00024 private $supported_importers = array();
00025
00026 private $default_values = array(
00027 'db_name' => '',
00028 'db_host' => 'localhost',
00029 'db_user' => '',
00030 'db_pass' => '',
00031 'db_prefix' => 'wp_',
00032 'category_import' => true,
00033 'import_index' => 0,
00034 'error' => '',
00035 );
00036
00041 public function action_init ( ) {
00042
00043 $this->supported_importers[] = _t('WordPress Database');
00044
00045 }
00046
00054 public function filter_import_names ( $import_names ) {
00055
00056 return array_merge( $import_names, $this->supported_importers );
00057
00058 }
00059
00070 public function filter_import_stage ( $stage_output, $import_name, $stage, $step ) {
00071
00072
00073 if ( !in_array( $import_name, $this->supported_importers ) ) {
00074
00075 return $stage_output;
00076 }
00077
00078
00079 $inputs = array();
00080
00081
00082 switch ( $stage ) {
00083
00084 case 1:
00085
00086 if ( isset( $_POST['wpimport'] ) ) {
00087
00088 $inputs = $_POST->filter_keys( array( 'db_name', 'db_user', 'db_host', 'db_pass', 'db_prefix', 'category_import', 'import_index' ) );
00089 $inputs = $inputs->getArrayCopy();
00090
00091
00092 if ( $this->wp_connect( $inputs['db_host'], $inputs['db_name'], $inputs['db_user'], $inputs['db_pass'] ) ) {
00093
00094
00095 $stage = 2;
00096
00097 }
00098 else {
00099
00100
00101 $inputs['error'] = _t('Could not connect to the WordPress database using the values supplied. Please correct them and try again.');
00102
00103 }
00104
00105 }
00106
00107 break;
00108
00109 }
00110
00111
00112
00113 switch ( $stage ) {
00114
00115 case 1:
00116 default:
00117 $output = $this->stage1( $inputs, $import_name );
00118 break;
00119
00120 case 2:
00121 $output = $this->stage2( $inputs );
00122 break;
00123
00124 }
00125
00126
00127 return $output;
00128
00129 }
00130
00140 private function stage1 ( $inputs, $import_name ) {
00141
00142 $inputs = array_merge( $this->default_values, $inputs );
00143
00144
00145 if ( $inputs['error'] != '' ) {
00146 $error = '<p class="error">' . $inputs['error'] . '</p>';
00147 }
00148 else {
00149
00150 $error = '';
00151 }
00152
00153 $output = '<p>' . _t( 'Habari will attempt to import from a WordPress database.') . '</p>';
00154 $output .= $error;
00155
00156
00157 $form = $this->get_form( $inputs, $import_name );
00158
00159
00160 $output .= $form->get();
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190 return $output;
00191
00192 }
00193
00200 private function stage2 ( $inputs ) {
00201
00202
00203 $inputs = array_merge( $this->default_values, $inputs );
00204
00205
00206 $ajax_url = URL::get( 'auth_ajax', array( 'context' => 'wp_import_users' ) );
00207
00208
00209 $vars = $inputs;
00210
00211 EventLog::log( _t('Starting import from "%s"', array( $inputs['db_name'] ) ) );
00212
00213 $output = '<p>' . _t('Import in Progress') . '</p>';
00214 $output .= '<div id="import_progress">' . _t( 'Starting Import…' ) . '</div>';
00215 $output .= $this->get_ajax( $ajax_url, $vars );
00216
00217 return $output;
00218
00219 }
00220
00228 private function get_ajax ( $url, $vars = array() ) {
00229
00230
00231 $ajax_vars = array();
00232 foreach ( $vars as $k => $v ) {
00233 $ajax_vars[] = $k . ': "' . $v . '"';
00234 }
00235 $ajax_vars = implode( ',', $ajax_vars );
00236
00237 $output = <<<WP_IMPORT_AJAX
00238 <script type="text/javascript">
00239 $(document).ready( function() {
00240 $('#import_progress').load(
00241 "{$url}",
00242 {
00243 {$ajax_vars}
00244 }
00245 );
00246 } );
00247 </script>
00248 WP_IMPORT_AJAX;
00249
00250 return $output;
00251
00252 }
00253
00261 private function get_form ( $inputs, $import_name ) {
00262
00263 $form = new FormUI('wp_importer');
00264
00265 $form->append( FormControlStatic::create( '<p>' . _t( 'Habari will attempt to import from a WordPress database.') . '</p>' ) );
00266
00267 $form->append( FormControlLabel::wrap( _t( 'Database Name'),
00268 FormControlText::create( 'db_name' )->set_value( $inputs['db_name'] )
00269 ));
00270
00271 $form->append( FormControlLabel::wrap( _t( 'Database Host'),
00272 FormControlText::create( 'db_host' )->set_value( $inputs['db_host'] )
00273 ));
00274
00275 $form->append( FormControlLabel::wrap( _t( 'Database User'),
00276 FormControlText::create( 'db_user' )->set_value( $inputs['db_user'] )
00277 ));
00278
00279 $form->append( FormControlLabel::wrap( _t( 'Database Password'),
00280 FormControlText::create( 'db_pass' )->set_value( $inputs['db_pass'] )
00281 ));
00282
00283 $form->append( FormControlLabel::wrap( _t( 'Table Prefix'),
00284 FormControlText::create( 'db_prefix' )->set_value( $inputs['db_prefix'] )
00285 ));
00286
00287 $form->append( FormControlLabel::wrap( _t( 'Import Categories as Tags' ),
00288 FormControlCheckbox::create( 'category_import' )->set_value( $inputs['category_import'] )
00289 ));
00290
00291 $form->append( FormControlData::create( 'stage' )->set_value( 1 ) );
00292
00293 $form->append( FormControlHidden::create( 'importer' )->set_value( $import_name ) );
00294
00295 $form->append( FormControlSubmit::create( 'wpimport' )->set_caption( _t( 'Import' ) )->add_class( 'button' ) );
00296
00297 $form->set_wrap_each( '<div>%s</div>' );
00298
00299 return $form;
00300
00301 }
00302
00313 private function wp_connect ( $db_host, $db_name, $db_user, $db_pass ) {
00314
00315
00316 $connection_string = 'mysql:host=' . $db_host . ';dbname=' . $db_name;
00317
00318 try {
00319 $wpdb = DatabaseConnection::ConnectionFactory( $connection_string );
00320 $wpdb->connect( $connection_string, $db_user, $db_pass );
00321
00322
00323
00324 return $wpdb;
00325 }
00326 catch ( \Exception $e ) {
00327
00328 return false;
00329 }
00330
00331 }
00332
00338 public function action_auth_ajax_wp_import_users ( ) {
00339
00340
00341 $inputs = $_POST->filter_keys( array( 'db_name', 'db_host', 'db_user', 'db_pass', 'db_prefix', 'category_import', 'import_index' ) );
00342 $inputs = $inputs->getArrayCopy();
00343
00344
00345 $inputs = array_merge( $this->default_values, $inputs );
00346
00347
00348 $wpdb = $this->wp_connect( $inputs['db_host'], $inputs['db_name'], $inputs['db_user'], $inputs['db_pass'] );
00349
00350
00351 if ( !$wpdb ) {
00352 EventLog::log( _t( 'Failed to import from "%s"', array( $inputs['db_name'] ) ) );
00353 Session::error( _t( 'Failed to import from "%s"', array( $inputs['db_name'] ) ) );
00354 echo '<p>' . _t( 'Failed to connect using the given database connection details.' ) . '</p>';
00355 }
00356
00357
00358
00359
00360 DB::begin_transaction();
00361
00362
00363 $wp_users = $wpdb->get_results( 'select id, user_login, user_pass, user_email, user_url, display_name from ' . $inputs['db_prefix'] . 'users' );
00364
00365 echo '<p>' . _t( 'Importing Users…' ) . '</p>';
00366
00367 foreach ( $wp_users as $wp_user ) {
00368
00369
00370 $user = User::get_by_name( $wp_user->user_login );
00371
00372 if ( $user !== false ) {
00373
00374
00375 $user->info->wp_id = intval( $wp_user->id );
00376
00377
00378 $user->update();
00379
00380 echo '<p>' . _t( 'Associated imported user %1$s with existing user %2$s', array( $wp_user->user_login, $user->username ) ) . '</p>';
00381
00382 EventLog::log( _t( 'Associated imported user %1$s with existing user %2$s', array( $wp_user->user_login, $user->username ) ) );
00383
00384 }
00385 else {
00386
00387
00388 try {
00389
00390 $u = new User();
00391 $u->username = $wp_user->user_login;
00392 $u->email = $wp_user->user_email;
00393
00394
00395 $u->password = Utils::crypt( $wp_user->user_pass );
00396
00397 $u->info->wp_id = intval( $wp_user->id );
00398 $u->info->displayname = $wp_user->display_name;
00399
00400 if ( $wp_user->user_url != '' ) {
00401 $u->info->url = $wp_user->user_url;
00402 }
00403
00404
00405 $u->insert();
00406
00407 echo '<p>' . _t( 'Created new user %1$s. Their old ID was %2$d.', array( $u->username, $wp_user->id ) ) . '</p>';
00408
00409 EventLog::log( _t( 'Created new user %1$s. Their old ID was %2$d.', array( $u->username, $wp_user->id ) ) );
00410
00411 }
00412 catch ( \Exception $e ) {
00413
00414
00415 EventLog::log( $e->getMessage, 'err' );
00416
00417 echo '<p class="error">' . _t( 'There was an error importing user %s. See the EventLog for the error message. ', array( $wp_user->user_login ) ) . '</p>';
00418
00419 echo '<p>' . _t( 'Rolling back changes…' ) . '</p>';
00420
00421
00422 DB::rollback();
00423
00424
00425 return false;
00426
00427 }
00428
00429 }
00430
00431 }
00432
00433
00434
00435 DB::commit();
00436
00437
00438 $ajax_url = URL::get( 'auth_ajax', array( 'context' => 'wp_import_posts' ) );
00439
00440
00441 echo $this->get_ajax( $ajax_url, $inputs );
00442
00443 }
00444
00450 public function action_auth_ajax_wp_import_posts ( ) {
00451
00452
00453 $inputs = $_POST->filter_keys( array( 'db_name', 'db_host', 'db_user', 'db_pass', 'db_prefix', 'category_import', 'import_index' ) );
00454 $inputs = $inputs->getArrayCopy();
00455
00456
00457 $inputs = array_merge( $this->default_values, $inputs );
00458
00459
00460 $wpdb = $this->wp_connect( $inputs['db_host'], $inputs['db_name'], $inputs['db_user'], $inputs['db_pass'] );
00461
00462
00463 if ( !$wpdb ) {
00464 EventLog::log( _t( 'Failed to import from "%s"', array( $inputs['db_name'] ) ) );
00465 Session::error( _t( 'Failed to import from "%s"', array( $inputs['db_name'] ) ) );
00466 echo '<p>' . _t( 'Failed to connect using the given database connection details.' ) . '</p>';
00467 }
00468
00469
00470
00471
00472 DB::begin_transaction();
00473
00474
00475 $num_posts = $wpdb->get_value( 'select count(id) from ' . $inputs['db_prefix'] . 'posts' );
00476
00477
00478 $min = $inputs['import_index'] * IMPORT_BATCH;
00479 $max = min( $min + IMPORT_BATCH, $num_posts );
00480
00481
00482 echo '<p>' . _t( 'Importing posts %1$d - %2$d of %3$d.', array( $min, $max, $num_posts ) ) . '</p>';
00483
00484
00485 $users = DB::get_results( 'select user_id, value from {userinfo} where name = :name', array( ':name' => 'wp_id' ) );
00486
00487
00488 $user_map = array();
00489 foreach ( $users as $info ) {
00490 $user_map[ $info->value ] = $info->user_id;
00491 }
00492
00493
00494 $post_map = DB::get_column( 'select value from {postinfo} where name = :name', array( ':name' => 'wp_id' ) );
00495
00496
00497
00498
00499 $posts = $wpdb->get_results( 'select id, post_author, post_date, post_content, post_title, post_status, comment_status, post_name, post_modified, guid, post_type from ' . $inputs['db_prefix'] . 'posts order by id asc limit ' . $min . ', ' . IMPORT_BATCH );
00500
00501 foreach ( $posts as $post ) {
00502
00503
00504 if ( in_array( $post->id, $post_map ) ) {
00505 continue;
00506 }
00507
00508
00509
00510 $taxonomy_query = 'select name, slug from ' . $inputs['db_prefix'] . 'terms where term_id in ( select term_id from ' . $inputs['db_prefix'] . 'term_taxonomy where taxonomy = :taxonomy and term_taxonomy_id in ( select term_taxonomy_id from ' . $inputs['db_prefix'] . 'term_relationships where object_id = :object_id ) )';
00511
00512
00513 $tags = $wpdb->get_results( $taxonomy_query, array( ':taxonomy' => 'post_tag', ':object_id' => $post->id ) );
00514
00515
00516 if ( $inputs['category_import'] ) {
00517
00518 $categories = $wpdb->get_results( $taxonomy_query, array( ':taxonomy' => 'category', ':object_id' => $post->id ) );
00519 }
00520
00521
00522 $p = new Post( array(
00523 'title' => MultiByte::convert_encoding( $post->post_title ),
00524 'content' => MultiByte::convert_encoding( $post->post_content ),
00525 'user_id' => $user_map[ $post->post_author ],
00526 'pubdate' => DateTime::create( $post->post_date ),
00527 'updated' => DateTime::create( $post->post_modified ),
00528 'slug' => MultiByte::convert_encoding( $post->post_name ),
00529 ) );
00530
00531
00532 switch ( $post->post_type ) {
00533
00534 case 'post':
00535 $p->content_type = Post::type( 'entry' );
00536 break;
00537
00538 case 'page':
00539 $p->content_type = Post::type( 'page' );
00540 break;
00541
00542 default:
00543
00544 continue 2;
00545
00546 }
00547
00548
00549 switch ( $post->post_status ) {
00550
00551 case 'publish':
00552 $p->status = Post::status( 'published' );
00553 break;
00554
00555 case 'future':
00556 $p->status = Post::status( 'scheduled' );
00557 break;
00558
00559 case 'pending':
00560 case 'draft':
00561 $p->status = Post::status( 'draft' );
00562 break;
00563
00564 default:
00565
00566 $status = Post::status( $post->post_status );
00567
00568 if ( $status == false ) {
00569
00570 continue 2;
00571 }
00572 else {
00573 $p->status = $status;
00574 }
00575
00576 break;
00577
00578 }
00579
00580
00581 if ( $post->comment_status == 'closed' ) {
00582 $p->info->comments_disabled = true;
00583 }
00584
00585
00586 $p->info->wp_id = $post->id;
00587
00588
00589 $p->info->wp_guid = $post->guid;
00590
00591
00592
00593 try {
00594 $p->insert();
00595
00596
00597
00598
00599 if ( $inputs['category_import'] ) {
00600 $tags = array_merge( $tags, $categories );
00601 }
00602
00603
00604 foreach ( $tags as $tag ) {
00605
00606
00607 $t = Tags::get_by_slug( $tag->slug );
00608
00609
00610 if ( $t == false ) {
00611 $t = Tag::create( array(
00612 'term' => $tag->slug,
00613 'term_display' => $tag->name
00614 ) );
00615 }
00616
00617
00618 $t->associate( 'post', $p->id );
00619
00620 }
00621
00622 }
00623 catch ( \Exception $e ) {
00624
00625 EventLog::log( $e->getMessage(), 'err' );
00626
00627 echo '<p class="error">' . _t( 'There was an error importing post %s. See the EventLog for the error message.', array( $post->post_title ) );
00628
00629 echo '<p>' . _t( 'Rolling back changes…' ) . '</p>';
00630
00631
00632 DB::rollback();
00633
00634
00635 return false;
00636
00637 }
00638
00639 }
00640
00641
00642
00643 DB::commit();
00644
00645 if ( $max < $num_posts ) {
00646
00647
00648
00649
00650 $ajax_url = URL::get( 'auth_ajax', array( 'context' => 'wp_import_posts' ) );
00651
00652
00653 $inputs['import_index']++;
00654
00655
00656 }
00657 else {
00658
00659
00660
00661
00662 $ajax_url = URL::get( 'auth_ajax', array( 'context' => 'wp_import_comments' ) );
00663
00664
00665 $inputs['import_index'] = 0;
00666
00667 }
00668
00669
00670 echo $this->get_ajax( $ajax_url, $inputs );
00671
00672 }
00673
00679 public function action_auth_ajax_wp_import_comments ( ) {
00680
00681
00682 $inputs = $_POST->filter_keys( array( 'db_name', 'db_host', 'db_user', 'db_pass', 'db_prefix', 'category_import', 'import_index' ) );
00683 $inputs = $inputs->getArrayCopy();
00684
00685
00686 $inputs = array_merge( $this->default_values, $inputs );
00687
00688
00689 $wpdb = $this->wp_connect( $inputs['db_host'], $inputs['db_name'], $inputs['db_user'], $inputs['db_pass'] );
00690
00691
00692 if ( !$wpdb ) {
00693 EventLog::log( _t( 'Failed to import from "%s"', array( $inputs['db_name'] ) ) );
00694 Session::error( _t( 'Failed to import from "%s"', array( $inputs['db_name'] ) ) );
00695 echo '<p>' . _t( 'Failed to connect using the given database connection details.' ) . '</p>';
00696 }
00697
00698
00699
00700
00701 DB::begin_transaction();
00702
00703
00704 $num_comments = $wpdb->get_value( 'select count(comment_id) from ' . $inputs['db_prefix'] . 'comments' );
00705
00706
00707 $min = $inputs['import_index'] * IMPORT_BATCH;
00708 $max = min( $min + IMPORT_BATCH, $num_comments );
00709
00710
00711 echo '<p>' . _t( 'Importing comments %1$d - %2$d of %3$d.', array( $min, $max, $num_comments ) ) . '</p>';
00712
00713
00714 $users = DB::get_results( 'select user_id, value from {userinfo} where name = :name', array( ':name' => 'wp_id' ) );
00715
00716
00717 $user_map = array();
00718 foreach ( $users as $info ) {
00719 $user_map[ $info->value ] = $info->user_id;
00720 }
00721
00722
00723 $posts = DB::get_results( 'select post_id, value from {postinfo} where name = :name', array( ':name' => 'wp_id' ) );
00724
00725
00726 $post_map = array();
00727 foreach ( $posts as $info ) {
00728 $post_map[ $info->value ] = $info->post_id;
00729 }
00730
00731
00732 $comment_map = DB::get_column( 'select value from {commentinfo} where name = :name', array( ':name' => 'wp_id' ) );
00733
00734
00735
00736
00737 $comments = $wpdb->get_results( 'select comment_id, comment_post_id, comment_author, comment_author_email, comment_author_url, comment_author_ip, comment_date, comment_content, comment_karma, comment_approved, comment_agent, comment_type, comment_parent, user_id from ' . $inputs['db_prefix'] . 'comments order by comment_id asc limit ' . $min . ', ' . IMPORT_BATCH );
00738
00739 foreach ( $comments as $comment ) {
00740
00741
00742 if ( in_array( $comment->id, $comment_map ) ) {
00743 continue;
00744 }
00745
00746
00747 if ( !isset( $post_map[ $comment->comment_post_id ] ) ) {
00748 continue;
00749 }
00750
00751
00752 $c = new Comment( array(
00753 'content' => MultiByte::convert_encoding( $comment->comment_content ),
00754 'name' => MultiByte::convert_encoding( $comment->comment_author ),
00755 'email' => MultiByte::convert_encoding( $comment->comment_author_email ),
00756 'url' => MultiByte::convert_encoding( $comment->comment_author_url ),
00757 'date' => DateTime::create( $comment->comment_date ),
00758 'post_id' => $post_map[ $comment->comment_post_id ],
00759 ) );
00760
00761
00762 switch ( $comment->comment_type ) {
00763
00764 case 'pingback':
00765 $c->type = Comment::type( 'pingback' );
00766 break;
00767
00768 case 'trackback':
00769 $c->type = Comment::type( 'trackback' );
00770 break;
00771
00772 default:
00773 case 'comment':
00774 $c->type = Comment::type( 'comment' );
00775 break;
00776
00777 }
00778
00779
00780 switch ( $comment->comment_approved ) {
00781
00782 case '1':
00783 $c->status = Comment::status( 'approved' );
00784 break;
00785
00786 case '':
00787 case '0':
00788 $c->status = Comment::status( 'unapproved' );
00789 break;
00790
00791 case 'spam':
00792 $c->status = Comment::status( 'spam' );
00793 break;
00794
00795 default:
00796
00797 $status = Comment::status( $comment->comment_status );
00798
00799 if ( $status == false ) {
00800
00801 continue 2;
00802 }
00803 else {
00804 $c->status = $status;
00805 }
00806
00807 break;
00808
00809 }
00810
00811
00812 $c->info->wp_id = $comment->comment_id;
00813
00814
00815 $c->info->wp_post_id = $comment->comment_post_id;
00816
00817
00818 if ( $comment->comment_karma != '0' ) {
00819 $c->info->wp_karma = $comment->comment_karma;
00820 }
00821
00822
00823 if ( $comment->comment_agent != '' ) {
00824 $c->info->wp_agent = $comment->comment_agent;
00825 }
00826
00827
00828
00829 try {
00830 $c->insert();
00831 }
00832 catch ( \Exception $e ) {
00833
00834 EventLog::log( $e->getMessage(), 'err' );
00835
00836 echo '<p class="error">' . _t( 'There was an error importing comment ID %d. See the EventLog for the error message.', array( $comment->comment_id ) );
00837
00838 echo '<p>' . _t( 'Rolling back changes…' ) . '</p>';
00839
00840
00841 DB::rollback();
00842
00843
00844 return false;
00845
00846 }
00847
00848 }
00849
00850
00851
00852 DB::commit();
00853
00854 if ( $max < $num_comments ) {
00855
00856
00857
00858
00859 $ajax_url = URL::get( 'auth_ajax', array( 'context' => 'wp_import_comments' ) );
00860
00861
00862 $inputs['import_index']++;
00863
00864
00865 }
00866 else {
00867
00868
00869
00870 EventLog::log( _t( 'Import completed from "%s"', array( $inputs['db_name'] ) ) );
00871 echo '<p>' . _t( 'Import is complete.' ) . '</p>';
00872
00873 return;
00874
00875 }
00876
00877
00878 echo $this->get_ajax( $ajax_url, $inputs );
00879
00880 }
00881
00882
00883 }
00884
00885 ?>