Ticket #7543: atompub-importer.diff
| File atompub-importer.diff, 49.9 KB (added by cavemonkey50, 5 years ago) |
|---|
-
wp-admin/import/typepad.php
1 <?php 2 3 class Typepad_Import { 4 5 // General 6 var $username; 7 var $password; 8 9 // XML parsing 10 var $parser; 11 var $inside_tag = ''; 12 var $active_tag = ''; 13 var $entry_tag = ''; 14 var $last = ''; 15 var $start_byte = ''; 16 var $first_child = 0; 17 var $inside_entry = 0; 18 var $channel = array(); 19 var $items = array(); 20 var $item = array(); 21 22 // The data 23 var $posts = array(); 24 var $site_url; 25 var $next_url; 26 var $xmlrpc_url; 27 var $post_count = 0; 28 var $current_post = 0; 29 var $first_run = true; 30 31 function header() { 32 echo '<div class="wrap">'; 33 echo '<h2>'.__('Import TypePad').'</h2>'; 34 } 35 36 function footer() { 37 echo '</div>'; 38 } 39 40 function greet() { 41 $this->header(); 42 ?> 43 <div class="narrow"> 44 <p><?php _e('Howdy! We’re about to begin importing all of your TypePad entries into WordPress. To begin, enter the URL of your blog followed by the username and password of an administrator account. When you’re finished, click "Start Importing".'); ?></p> 45 46 <form method="post" action="<?php echo add_query_arg('step', 1); ?>" class="import-upload-form"> 47 48 <?php wp_nonce_field('import-typepad'); ?> 49 <table class="form-table"> 50 <tr> 51 <td><label for="mturl"><?php _e('Blog URL:') ?></label></td> 52 <td><input type="text" style="width:300px" name="mturl" id="mturl" value="http://" /></td> 53 </tr> 54 <tr> 55 <td><label for="mtuser"><?php _e('Username:') ?></label></td> 56 <td><input type="text" style="width:300px" name="mtuser" id="mtuser" value="" /></td> 57 </tr> 58 <tr> 59 <td><label for="mtpass"><?php _e("Password:") ?></label></td> 60 <td><input type="password" style="width:300px" name="mtpass" id="mtpass" value="" /></td> 61 </tr> 62 </table> 63 <p class="submit"> 64 <input type="submit" value="<?php echo attribute_escape(__('Start Importing')); ?>" id="atom-submit" onclick="jQuery('#atom-loading').show();jQuery('#atom-submit').val('<?php echo attribute_escape(__('Importing...')); ?>');" /> <span style="display: none;" id="atom-loading"><img src="<?php echo get_option('home'); ?>/wp-admin/images/loading.gif" alt="Loading" style="margin-bottom: -3px;" /> <?php echo attribute_escape(__('Retrieving entries from server. Do not navigate away from this page.')); ?></span> 65 </p> 66 </form> 67 <p><?php _e('The importer is smart enough not to import duplicates, so you can run this multiple times without worry if—for whatever reason—it doesn\'t finish.'); ?> </p> 68 </div> 69 <?php 70 $this->footer(); 71 } 72 73 function detect_atom_uri($url) { 74 // Grab the homepage 75 $ch = curl_init(); 76 curl_setopt($ch, CURLOPT_URL, $url); 77 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 78 curl_setopt($ch, CURLOPT_TIMEOUT, 5); 79 $output = @curl_exec($ch); 80 81 // Parse out the RSD URL 82 preg_match('#<link rel="EditURI" type="application\/rsd\+xml" title="RSD" href="(.*)" \/>#i', $output, $matches); 83 84 // Grab the RSD page 85 $ch2 = curl_init(); 86 curl_setopt($ch2, CURLOPT_URL, $matches[1]); 87 curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); 88 curl_setopt($ch2, CURLOPT_TIMEOUT, 5); 89 $output = @curl_exec($ch2); 90 91 // Parse out the xmlrpc URI 92 preg_match('#<api name="MovableType" preferred="false" apiLink="([^"]*)" blogid="([^"]*)" \/>#i', $output, $xmlrpc); 93 $this->xmlrpc_url = $xmlrpc[1]; 94 95 // Parse out the Atom URI 96 preg_match('#<api name="Atom" preferred="false" apiLink="(.*)" \/>#i', $output, $matches); 97 98 return $matches[1]; 99 } 100 101 function fetch_atom($url, $username, $password) { 102 include_once(ABSPATH . '/wp-admin/includes/class-atom-api.php'); 103 104 $atom_api = new AtomAPI($url, $username, $password, 'WSSE'); // Authenticate to the AtomPub feed using WSSE 105 // If the login failed, throw an error message 106 if ( ( $atom_api->err_no != '' ) && ( count($this->posts) == 0 ) ) { 107 $this->error(); 108 return 'error'; 109 } 110 111 // Grab the raw feed data and pass it to the parser 112 $this->parse_atom( $atom_api->get_feeds( $xml = false ) ); 113 } 114 115 function parse_atom($xml) { 116 // Reset the data before parsing a new feed 117 unset($this->parser); 118 unset($this->inside_tag); 119 unset($this->active_tag); 120 unset($this->channel); 121 unset($this->items); 122 unset($this->item); 123 $this->inside_tag = ''; 124 $this->active_tag = ''; 125 $this->entry_tag = ''; 126 $this->last = ''; 127 $this->start_byte = ''; 128 $this->first_child = 0; 129 $this->inside_entry = 0; 130 $this->channel = array(); 131 $this->items = array(); 132 $this->item = array(); 133 134 // Create an XML parser 135 $this->parser = xml_parser_create(); 136 137 // Used to make the XML parser work in a class 138 xml_set_object($this->parser, $this); 139 140 // Set the start and ending element functions 141 xml_set_element_handler($this->parser, "start_element", "end_element"); 142 143 // Set the character data functions 144 xml_set_character_data_handler($this->parser, "character_data"); 145 146 // Begin parsing the XML 147 xml_parse($this->parser, $xml) 148 or die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser))); 149 150 // Clean up after the parse 151 xml_parser_free($this->parser); 152 } 153 154 function start_element($parser, $element, $attrs) { 155 switch ($element) { 156 case 'FEED': 157 case 'ENTRY': 158 $this->inside_tag = $element; 159 break; 160 161 case 'CONTENT': 162 case 'SUMMARY': 163 if ( $this->inside_tag == 'ENTRY' ) { 164 $this->inside_entry = 1; 165 $this->entry_tag = strtolower($element); 166 } 167 break; 168 169 case 'LINK' : 170 if ( $this->inside_tag == 'FEED' ) { 171 if ( $attrs['REL'] == 'alternate' || !$attrs['REL'] ) { 172 $this->_add('channel', 'link', $attrs['HREF']); 173 } elseif ( $attrs['REL'] == 'next' ) { 174 $this->_add('channel', 'next', $attrs['HREF']); 175 } 176 } elseif ( $this->inside_tag == 'ENTRY' ) { 177 if ( $attrs['REL'] == 'alternate' || !$attrs['REL'] ) { 178 if ( $attrs['TYPE'] == 'text/html' ) 179 $this->_add('item', 'link', $attrs['HREF']); 180 } elseif ( $attrs['REL'] == 'service.edit' ) { 181 $this->_add('item', 'link:service.edit', $attrs['HREF']); 182 } elseif ( $attrs['REL'] == 'replies' ) { 183 $this->_add('item', 'link:entry.comments', $attrs['HREF']); 184 } 185 } elseif ( $this->inside_tag != 'FEED' && $this->inside_tag != 'ENTRY' ) { 186 if ( $attrs['REL'] == 'next' ) { 187 $this->_add('channel', 'next', $attrs['HREF']); 188 } 189 } 190 break; 191 192 case 'CATEGORY': 193 if ( $this->inside_tag == 'ENTRY' ) { 194 $cats = array ( 'type' => $attrs['SCHEME'], 'term' => $attrs["TERM"] ); 195 $this->_add('item', 'categories', $cats, 1); 196 } 197 break; 198 199 case 'AUTHOR': 200 if ( $this->inside_tag == 'ENTRY' ) { 201 $this->inside_tag = 'ENTRY|AUTHOR'; 202 } 203 break; 204 205 case 'ALLOW': 206 if ( $this->inside_tag == 'ENTRY' ) { 207 $this->_add('item', 'privacy', $attrs['NAME']); 208 } 209 break; 210 211 default : 212 $this->active_tag = $element; 213 } 214 215 // Deal with tags inside of content and summary 216 if ( $this->inside_entry && $element != 'CONTENT' && $element != 'SUMMARY' ) { 217 // If tags are inlined, then flatten 218 $attrs = array_change_key_case($attrs, CASE_LOWER); 219 $attrs_str = implode(' ', 220 array_map(array($this, 'map_attrs'), 221 array_keys($attrs), 222 array_values($attrs) ) ); 223 224 // Add a space if there are attrs 225 if ( $attrs_str != '' ) 226 $attrs_str = ' ' . $attrs_str; 227 228 // Make the element lowercase 229 $el = strtolower($element); 230 231 // Close an opening tag if it had no data 232 if ( $this->first_child ) 233 $this->_add('item', $this->entry_tag, ">"); 234 235 // Add the start tag 236 $this->_add('item', $this->entry_tag, "<$el" . "$attrs_str"); 237 238 // Handle self-closing tags 239 $this->first_child = 1; 240 $this->start_byte = xml_get_current_byte_index($parser); 241 } 242 } 243 244 function character_data($parser, $cdata) { 245 switch ($this->inside_tag) { 246 // Grab general channel information 247 case 'FEED': 248 switch ($this->active_tag) { 249 case 'TITLE': 250 $this->_add('channel', strtolower($this->active_tag), $cdata); 251 break; 252 case 'OPENSEARCH:TOTALRESULTS': 253 $this->_add('channel', strtolower('fullcount'), $cdata); 254 break; 255 } 256 break; 257 258 // Grab item information 259 case 'ENTRY': 260 switch ($this->active_tag) { 261 case 'TITLE': 262 $this->_add('item', strtolower($this->active_tag), $cdata); 263 break; 264 265 case 'CONTENT': 266 case 'SUMMARY': 267 $this->_add('item', $this->inside_tag, $cdata); 268 break; 269 270 case 'ISSUED': 271 $this->_add('item', strtolower($this->active_tag), $cdata); 272 break; 273 274 case 'ID': 275 $this->_add('item', strtolower($this->active_tag), $cdata); 276 break; 277 } 278 break; 279 280 // For author tags 281 case 'ENTRY|AUTHOR': 282 switch ($this->active_tag) { 283 case 'NAME': 284 $this->_add('item', 'author', $cdata); 285 break; 286 case 'URI': 287 $this->_add('item', 'author_uri', $cdata); 288 break; 289 } 290 break; 291 292 } 293 294 // Deal with data inside content tags 295 if ( $this->inside_entry ) { 296 // Append the start tag close if first_child 297 if ( $this->first_child ) { 298 $this->_add('item', $this->entry_tag, '>'); 299 $this->first_child = 0; 300 } 301 302 $this->_add('item', $this->entry_tag, $cdata); 303 } 304 } 305 306 function end_element($parser, $element) { 307 // Deal with tags inside of content and summary 308 if ( $this->inside_entry && $element != 'CONTENT' && $element != 'SUMMARY' ) { 309 $el = strtolower($element); 310 311 // Add the appropriate end tag depending if the element is a self-closing element 312 if ( xml_get_current_byte_index($parser) - $this->start_byte > 2 ) { 313 if ( $this->first_child ) 314 $this->_add('item', $this->entry_tag, ">"); 315 $this->_add('item', $this->entry_tag, "</$el>"); 316 } else { 317 $this->_add('item', $this->entry_tag, " />"); 318 } 319 $this->first_child = 0; 320 } 321 322 // Reset inside tag back to entry for author tags 323 if ( $element == 'AUTHOR' && $this->inside_tag == 'ENTRY|AUTHOR' ) { 324 $this->inside_tag = 'ENTRY'; 325 } 326 327 if ( $element == $this->inside_tag ) { 328 $this->inside_tag = ''; 329 $this->struct[] = array_merge(array('type' => strtolower($element)), $this->last); 330 } 331 332 if ( $element == 'ENTRY' ) { 333 $this->items[] = $this->item; 334 $this->item = ''; 335 } 336 337 if ( $element == 'CONTENT' || $element == 'SUMMARY' ) { 338 $this->inside_entry = 0; 339 } 340 341 $this->active_tag = ''; 342 } 343 344 function _add($type, $field, $value, $array = 0) { 345 if ( $array ) { 346 $this->{$type}[$field][] = $value; 347 } else { 348 if ( empty($this->{$type}) || empty($this->{$type}[$field]) ) { 349 $this->{$type}[$field] = $value; 350 } else { 351 $this->{$type}[$field] .= $value; 352 } 353 } 354 355 $this->last = $this->{$type}; 356 } 357 358 function map_attrs($k, $v) { 359 return "$k=\"$v\""; 360 } 361 362 function get_entries() { 363 // Set the URL 364 if ( $this->next_url != '' ) 365 $url = $this->next_url; 366 else 367 $url = $this->detect_atom_uri(trim($_POST['mturl'])); 368 369 // Get the entries from the atom feed 370 $result = $this->fetch_atom($url, $this->username, $this->password); 371 if ( $result == 'error' ) return $result; 372 373 // Set the site URL and next URLs 374 $this->site_url = $this->channel['link']; 375 $this->next_url = $this->channel['next']; 376 $this->post_count = $this->channel['fullcount']; 377 378 // Convert the parsed atom feed to the posts / pages array 379 foreach ( $this->items AS $item ) { 380 $this->posts[] = $item; 381 } 382 } 383 384 function get_comments($comment_feed) { 385 $comments = array(); 386 387 // While there are more comments 388 do { 389 390 // Select the right comments feed URL 391 if ( $comments_next != '' ) 392 $comment_feed = $comments_next; 393 394 // Get the comments from the atom feed 395 if ( !empty($comment_feed) ) 396 $this->fetch_atom($comment_feed, $this->username, $this->password); 397 398 // The next URL 399 $comments_next = $this->channel['next']; 400 401 // Convert the parsed atom feed to the comments array 402 foreach ( $this->items AS $item ) { 403 $comments[] = $item; 404 } 405 406 } while ( $comments_next != '' ); 407 408 return $comments; 409 } 410 411 function get_trackbacks($post_id) { 412 include_once(ABSPATH . WPINC . '/class-IXR.php'); 413 414 $rpc = new IXR_Client($this->xmlrpc_url); 415 if ( !$rpc->query('mt.getTrackbackPings', $post_id) ) { 416 // print("Error (" . $rpc->getErrorCode() . "): "); 417 // print($rpc->getErrorMessage() . "\n\n"); 418 } 419 420 return $rpc->getResponse(); 421 } 422 423 function get_additional_data($post_id) { 424 include_once(ABSPATH . WPINC . '/class-IXR.php'); 425 426 $rpc = new IXR_Client($this->xmlrpc_url); 427 if ( !$rpc->query('metaWeblog.getPost', $post_id, $this->username, $this->password) ) { 428 // print("Error (" . $rpc->getErrorCode() . "): "); 429 // print($rpc->getErrorMessage() . "\n\n"); 430 } 431 432 return $rpc->getResponse(); 433 } 434 435 function process_posts() { 436 // Start the output list if this is the first run 437 if ( $this->first_run == true ) { 438 echo '<div class="wrap"><ol>'; 439 $this->first_run = false; 440 } 441 442 // Use output buffering to show the current import progress 443 if (ob_get_level() == 0) { 444 ob_start(); 445 } 446 // Pad the page with some data so output buffering works across browsers 447 echo str_pad('',4096); 448 449 foreach ( $this->posts AS $entry ) { 450 451 // Construct the post class from the AtomPub feed 452 $post = new StdClass(); 453 454 // The entry type (over 1 and we have a post) 455 if ( count(explode('/', substr($entry['link'], strlen($this->site_url)))) > 1 ) 456 $post->post_type = 'post'; 457 else 458 $post->post_type = 'page'; 459 // The entry title 460 if ( isset($entry['title']) ) 461 $post->post_title = trim($entry['title']); 462 else 463 $post->post_title = ''; 464 // The post content 465 if ( isset($entry['content']) ) 466 $post->post_content = trim($entry['content']); 467 // The post excerpt 468 if ( isset($entry['summary']) ) 469 $post->post_excerpt = trim($entry['summary']); 470 // The post date 471 $date = strtotime(trim($entry['issued'])); 472 $date = date('Y-m-d H:i:s', $date); 473 $date_gmt = get_gmt_from_date($date); 474 $post->post_modified = $date; 475 $post->post_modified_gmt = $date_gmt; 476 $post->post_date = $date; 477 $post->post_date_gmt = $date_gmt; 478 // The post slug 479 $slug = array_reverse(explode('/', trim($entry['link']))); 480 $slug = substr($slug[0], 0 , -5); 481 $post->post_name = $slug; 482 // The categories and tags 483 if ( isset($entry['categories']) ) { 484 foreach ( $entry['categories'] AS $cat ) { 485 $post->categories[] = trim($cat['term']); 486 } 487 } else { 488 $post->categories = array(); 489 } 490 // The post author 491 if ( isset($entry['author']) ) 492 $post->post_author = trim($entry['author']); 493 // The post id 494 $post_id = trim($entry['id']); 495 $post_id = substr(strrchr($post_id, '-'), 1); 496 // The additional data 497 $additional_data = $this->get_additional_data($post_id); 498 // The comment status 499 if ( $additional_data['mt_allow_comments'] == 1 ) 500 $post->comment_status = 'open'; 501 else 502 $post->comment_status = 'closed'; 503 if ( $additional_data['mt_allow_pings'] == 1 ) 504 $post->ping_status = 'open'; 505 else 506 $post->ping_status = 'closed'; 507 // The tags 508 $post->post_keywords = $additional_data['mt_keywords']; 509 // The draft status 510 if ( isset($entry['privacy']) ) { 511 if ( $entry['privacy'] == 'Self' ) 512 $post->post_status = 'draft'; 513 else 514 $post->post_status = 'publish'; 515 } 516 517 unset($comments); 518 $comments = array(); 519 520 // Fetch the comments if the post has comments 521 $coms = $this->get_comments(trim($entry['link:entry.comments'])); 522 if ( count($coms) > 0 ) { 523 foreach ( $coms AS $com ) { 524 // Construct the comment class from the AtomPub feed 525 $comment = new StdClass(); 526 527 // The comment date 528 $date = strtotime(trim($com['issued'])); 529 $date = date('Y-m-d H:i:s', $date); 530 $comment->comment_date = $date; 531 // The comment content 532 $comment->comment_content = trim($com['content']); 533 // The comment author 534 $comment->comment_author = trim($com['author']); 535 // The comment author uri 536 $comment->comment_author_url = trim($com['author_uri']); 537 // Set the unknown variables 538 $comment->comment_author_email = ''; 539 $comment->comment_author_IP = ''; 540 541 // Add the comment to the comments array 542 $comments[] = $comment; 543 } 544 } 545 546 unset($trackbacks); 547 $pings = array(); 548 549 // Fetch the trackbacks 550 $trackbacks = $this->get_trackbacks($post_id); 551 if ( count($trackbacks) > 0 ) { 552 foreach ( $trackbacks AS $trackback ) { 553 $ping = new StdClass(); 554 // The ping date (the date of the post since we don't know the real one) 555 $ping->comment_date = $post->post_date; 556 // The ping IP address 557 $ping->comment_author_IP = $trackback['pingIP']; 558 // The ping URL 559 $ping->comment_author_url = $trackback['pingURL']; 560 // The ping author 561 $ping->comment_author = $trackback['pingTitle']; 562 // Set the unknow variables 563 $ping->comment_author_email = ''; 564 $ping->comment_content = ''; 565 $ping->comment_type = 'pingback'; 566 567 // Add the ping to the pings array 568 $pings[] = $ping; 569 } 570 } 571 572 $result = $this->save_post($post, $comments, $pings); 573 if ( is_wp_error( $result ) ) 574 return $result; 575 576 // Increase the post count 577 $this->current_post++; 578 579 // Update the progress bar 580 $progress_bar = round(( $this->current_post / $this->post_count ) * 500); 581 $progress_percent = round(( $this->current_post / $this->post_count ) * 100, 1); 582 ?> 583 <script type="text/javascript"> 584 jQuery('.progress_bar .progress_display').animate({ 585 width: "<?php echo $progress_bar; ?>px" 586 }, 200); 587 jQuery('.progress_percent span.percent').html('<?php echo $progress_percent; ?>'); 588 <?php if ( $progress_percent == 100 ) { ?> 589 jQuery('.progress_percent img').hide(); 590 <?php } ?> 591 </script> 592 <?php 593 594 // Flush the output buffer to show current state 595 ob_flush(); 596 flush(); 597 usleep(50000); 598 } 599 600 // Clean up after using the output buffer 601 ob_end_flush(); 602 603 // If there are no more posts end the list tags 604 if ( $this->next_url == '' ) { 605 echo '</ol>'; 606 do_action('import_done', 'typepad'); 607 echo '<h3>'.sprintf(__('All done. <a href="%s">Have fun!</a>'), get_option('home')).'</h3></div>'; 608 } 609 } 610 611 function save_post(&$post, &$comments, &$pings) { 612 $post = get_object_vars($post); 613 $post = add_magic_quotes($post); 614 $post = (object) $post; 615 616 if ( $post_id = post_exists($post->post_title, '', $post->post_date) ) { 617 echo '<li>'; 618 printf(__('Post <em>%s</em> already exists.'), stripslashes($post->post_title)); 619 } else { 620 echo '<li>'; 621 printf(__('Importing post <em>%s</em>...'), stripslashes($post->post_title)); 622 623 $post->post_author = $this->check_author($post->post_author); //just so that if a post already exists, new users are not created by checkauthor 624 $post_id = wp_insert_post($post); 625 if ( is_wp_error( $post_id ) ) 626 return $post_id; 627 628 // Add categories. 629 if ( 0 != count($post->categories) ) { 630 wp_create_categories($post->categories, $post_id); 631 } 632 633 // Add tags or keywords 634 if ( 1 < strlen($post->post_keywords) ) { 635 // Keywords exist. 636 printf(__('<br />Adding tags <i>%s</i>...'), stripslashes($post->post_keywords)); 637 wp_add_post_tags($post_id, $post->post_keywords); 638 } 639 640 $num_comments = 0; 641 foreach ( $comments as $comment ) { 642 $comment = get_object_vars($comment); 643 $comment = add_magic_quotes($comment); 644 645 if ( !comment_exists($comment['comment_author'], $comment['comment_date'])) { 646 $comment['comment_post_ID'] = $post_id; 647 $comment = wp_filter_comment($comment); 648 wp_insert_comment($comment); 649 $num_comments++; 650 } 651 } 652 653 if ( $num_comments ) 654 printf(' '.__ngettext('(%s comment)', '(%s comments)', $num_comments), $num_comments); 655 656 $num_pings = 0; 657 foreach ( $pings as $ping ) { 658 $ping = get_object_vars($ping); 659 $ping = add_magic_quotes($ping); 660 661 if ( !comment_exists($ping['comment_author'], $ping['comment_date'])) { 662 $ping['comment_content'] = "<strong>{$ping['comment_author']}</strong>"; 663 $ping['comment_post_ID'] = $post_id; 664 $ping = wp_filter_comment($ping); 665 wp_insert_comment($ping); 666 $num_pings++; 667 } 668 } 669 670 if ( $num_pings ) 671 printf(' '.__ngettext('(%s ping)', '(%s pings)', $num_pings), $num_pings); 672 } 673 674 echo '</li>'; 675 } 676 677 function check_author($author) { 678 global $wpdb; 679 680 $user_id = username_exists($author); 681 682 // If the username does not exist 683 if ( !$user_id ) { 684 $pass = wp_generate_password(); 685 $user_id = wp_create_user($author, $pass); 686 } 687 688 return $user_id; 689 } 690 691 function error() { 692 ?> 693 <div class="narrow"> 694 <h3><?php _e('Import Failed'); ?></h3> 695 <p><?php _e('There was an error starting the import process. Please go back and verify that the URL, username, and password were entered correctly.'); ?></p> 696 <p><a href="<?php echo add_query_arg('step', 0); ?>"><?php _e('Restart the Import Process »')?></a></p> 697 </div> 698 <?php 699 } 700 701 function import() { 702 $this->header(); 703 704 // Add the username and password as a global 705 $this->username = $_POST['mtuser']; 706 $this->password = $_POST['mtpass']; 707 708 // Use output buffering so the progress bar appears immediately 709 if (ob_get_level() == 0) { 710 ob_start(); 711 } 712 713 // Pad the page with some data so output buffering works across browsers 714 echo str_pad('',4096); 715 716 ?> 717 <h3><?php _e('Import in Progress, Results Will Appear Momentarily'); ?></h3> 718 719 <p><?php _e('While the importer is running do not navigate away from this page.'); ?></p> 720 721 <div class="progress_bar" style="float:left; width: 500px; height; 20px; padding: 1px; border: 1px solid #dadada;"> 722 <div class="progress_display" style="width: 0px; background: #ccc"> </div> 723 </div> 724 <div class="progress_percent" style="padding: 2px;"> <img src="<?php echo get_option('home'); ?>/wp-admin/images/loading.gif" alt="Loading" style="margin-bottom: -3px;" /> <span class="percent">0</span>%</div> 725 <?php 726 727 // Clear the output buffer 728 ob_flush(); 729 flush(); 730 ob_end_flush(); 731 732 // While we have more posts 733 do { 734 735 $result = $this->get_entries(); 736 737 if ( $result != 'error' ) 738 $result = $this->process_posts(); 739 740 if ( is_wp_error( $result ) ) 741 echo $result->get_error_message(); 742 743 // Free memory for next round of post 744 unset($this->posts); 745 $this->posts = array(); 746 747 } while ( $this->next_url != '' ); 748 749 $this->footer(); 750 } 751 752 function dispatch() { 753 if (empty ($_GET['step'])) 754 $step = 0; 755 else 756 $step = (int) $_GET['step']; 757 758 switch ($step) { 759 case 0 : 760 $this->greet(); 761 break; 762 case 1 : 763 check_admin_referer('import-typepad'); 764 $this->import(); 765 break; 766 } 767 } 768 769 function Typepad_Import() { 770 // Nothing. 771 } 772 } 773 774 $typepad_import = new Typepad_Import(); 775 776 register_importer('typepad', __('TypePad'), __('Import posts, comments, and categories from a TypePad blog.'), array ($typepad_import, 'dispatch')); 777 ?> 778 No newline at end of file -
wp-admin/import/mt-atom.php
1 <?php 2 3 class MT_Atom_Import { 4 5 // General 6 var $username; 7 var $password; 8 9 // XML parsing 10 var $parser; 11 var $inside_tag = ''; 12 var $active_tag = ''; 13 var $entry_tag = ''; 14 var $last = ''; 15 var $start_byte = ''; 16 var $first_child = 0; 17 var $inside_entry = 0; 18 var $channel = array(); 19 var $items = array(); 20 var $item = array(); 21 22 // The data 23 var $posts = array(); 24 var $site_url; 25 var $xmlrpc_url; 26 var $next_url; 27 var $post_count = 0; 28 var $current_post = 0; 29 var $more_posts = true; 30 var $first_run = true; 31 32 function header() { 33 echo '<div class="wrap">'; 34 echo '<h2>'.__('Import Movable Type').'</h2>'; 35 } 36 37 function footer() { 38 echo '</div>'; 39 } 40 41 function greet() { 42 $this->header(); 43 ?> 44 <div class="narrow"> 45 <p><?php _e('Howdy! We’re about to begin importing all of your Movable Type entries into WordPress. To begin, enter the URL of your blog followed by the username and password of an administrator account. When you’re finished, click "Start Importing".'); ?></p> 46 <p><?php _e('<strong>Note:</strong> Use your web services password instead of your regular account password. To obtain this password, log in to your MT blog and follow these steps:'); ?></p> 47 <ol> 48 <li><?php _e('In the upper right, click Hi [username].'); ?></li> 49 <li><?php _e('Scroll down to the Preferences section.'); ?></li> 50 <li><?php _e('Under Web Services Password, click reveal.'); ?></li> 51 <li><?php _e('Enter the password displayed in the password box below.'); ?></li> 52 </ol> 53 54 <form method="post" action="<?php echo add_query_arg('step', 1); ?>" class="import-upload-form"> 55 56 <?php wp_nonce_field('import-mt-atom'); ?> 57 <table class="form-table"> 58 <tr> 59 <td><label for="mturl"><?php _e('Blog URL:') ?></label></td> 60 <td><input type="text" style="width:300px" name="mturl" id="mturl" value="http://" /></td> 61 </tr> 62 <tr> 63 <td><label for="mtuser"><?php _e('Username:') ?></label></td> 64 <td><input type="text" style="width:300px" name="mtuser" id="mtuser" value="" /></td> 65 </tr> 66 <tr> 67 <td><label for="mtpass"><?php _e("Password:") ?></label></td> 68 <td><input type="password" style="width:300px" name="mtpass" id="mtpass" value="" /></td> 69 </tr> 70 </table> 71 <p class="submit"> 72 <input type="submit" value="<?php echo attribute_escape(__('Start Importing')); ?>" id="atom-submit" onclick="jQuery('#atom-loading').show();jQuery('#atom-submit').val('<?php echo attribute_escape(__('Importing...')); ?>');" /> <span style="display: none;" id="atom-loading"><img src="<?php echo get_option('home'); ?>/wp-admin/images/loading.gif" alt="Loading" style="margin-bottom: -3px;" /> <?php echo attribute_escape(__('Retrieving entries from server. Do not navigate away from this page.')); ?></span> 73 </p> 74 </form> 75 <p><?php _e('The importer is smart enough not to import duplicates, so you can run this multiple times without worry if—for whatever reason—it doesn\'t finish.'); ?> </p> 76 </div> 77 <?php 78 $this->footer(); 79 } 80 81 function detect_atom_uri($url) { 82 // Set the RSD URL 83 $rsd_url = $url . '/rsd.xml'; 84 85 // Grab the RSD page 86 $ch2 = curl_init(); 87 curl_setopt($ch2, CURLOPT_URL, $rsd_url); 88 curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); 89 curl_setopt($ch2, CURLOPT_TIMEOUT, 5); 90 $output = @curl_exec($ch2); 91 92 // Parse out the xmlrpc URI 93 preg_match('#<api name="MovableType" preferred="false" apiLink="([^"]*)" blogid="([^"]*)" \/>#i', $output, $xmlrpc); 94 $this->xmlrpc_url = $xmlrpc[1]; 95 96 // Parse out the Atom URI 97 preg_match('#<api name="Atom" preferred="false" apiLink="([^"]*)" blogid="([^"]*)" \/>#i', $output, $matches); 98 99 return $matches[1] . '/blog_id=' . $matches[2]; 100 } 101 102 function fetch_atom($url, $username, $password) { 103 include_once(ABSPATH . '/wp-admin/includes/class-atom-api.php'); 104 105 $atom_api = new AtomAPI($url, $username, $password, 'WSSE'); // Authenticate to the AtomPub feed using WSSE 106 // If the login failed, throw an error message 107 if ( ( $atom_api->err_no != '' ) && ( count($this->posts) == 0 ) ) { 108 $this->error(); 109 return 'error'; 110 } 111 112 // Grab the raw feed data and pass it to the parser 113 $this->parse_atom( $atom_api->get_feeds( $xml = false ) ); 114 } 115 116 function parse_atom($xml) { 117 // Reset the data before parsing a new feed 118 unset($this->parser); 119 unset($this->inside_tag); 120 unset($this->active_tag); 121 unset($this->channel); 122 unset($this->items); 123 unset($this->item); 124 $this->inside_tag = ''; 125 $this->active_tag = ''; 126 $this->entry_tag = ''; 127 $this->last = ''; 128 $this->start_byte = ''; 129 $this->first_child = 0; 130 $this->inside_entry = 0; 131 $this->channel = array(); 132 $this->items = array(); 133 $this->item = array(); 134 135 // Create an XML parser 136 $this->parser = xml_parser_create(); 137 138 // Used to make the XML parser work in a class 139 xml_set_object($this->parser, $this); 140 141 // Set the start and ending element functions 142 xml_set_element_handler($this->parser, "start_element", "end_element"); 143 144 // Set the character data functions 145 xml_set_character_data_handler($this->parser, "character_data"); 146 147 // Begin parsing the XML 148 xml_parse($this->parser, $xml) 149 or die(sprintf("XML error: %s at line %d", xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser))); 150 151 // Clean up after the parse 152 xml_parser_free($this->parser); 153 } 154 155 function start_element($parser, $element, $attrs) { 156 switch ($element) { 157 case 'FEED': 158 case 'ENTRY': 159 $this->inside_tag = $element; 160 break; 161 162 case 'CONTENT': 163 case 'SUMMARY': 164 if ( $this->inside_tag == 'ENTRY' ) { 165 $this->inside_entry = 1; 166 $this->entry_tag = strtolower($element); 167 } 168 break; 169 170 case 'LINK' : 171 if ( $this->inside_tag == 'FEED' ) { 172 if ( $attrs['REL'] == 'alternate' || !$attrs['REL'] ) { 173 $this->_add('channel', 'link', $attrs['HREF']); 174 } elseif ( $attrs['REL'] == 'next' ) { 175 $this->_add('channel', 'next', $attrs['HREF']); 176 } 177 } elseif ( $this->inside_tag == 'ENTRY' ) { 178 if ( $attrs['REL'] == 'alternate' || !$attrs['REL'] ) { 179 $this->_add('item', 'link', $attrs['HREF']); 180 } elseif ( $attrs['REL'] == 'service.edit' ) { 181 $this->_add('item', 'link:service.edit', $attrs['HREF']); 182 } elseif ( $attrs['REL'] == 'replies' ) { 183 $this->_add('item', 'link:replies', $attrs['HREF']); 184 } 185 } elseif ( $this->inside_tag != 'FEED' && $this->inside_tag != 'ENTRY' ) { 186 if ( $attrs['REL'] == 'next' ) { 187 $this->_add('channel', 'next', $attrs['HREF']); 188 } 189 } 190 break; 191 192 case 'CATEGORY': 193 if ( $this->inside_tag == 'ENTRY' ) { 194 $cats = array ( 'type' => $attrs['SCHEME'], 'term' => $attrs["TERM"] ); 195 $this->_add('item', 'categories', $cats, 1); 196 } 197 break; 198 199 case 'AUTHOR': 200 if ( $this->inside_tag == 'ENTRY' ) { 201 $this->inside_tag = 'ENTRY|AUTHOR'; 202 } 203 break; 204 205 default : 206 $this->active_tag = $element; 207 } 208 209 // Deal with tags inside of content and summary 210 if ( $this->inside_entry && $element != 'CONTENT' && $element != 'SUMMARY' ) { 211 // If tags are inlined, then flatten 212 $attrs = array_change_key_case($attrs, CASE_LOWER); 213 $attrs_str = implode(' ', 214 array_map(array($this, 'map_attrs'), 215 array_keys($attrs), 216 array_values($attrs) ) ); 217 218 // Add a space if there are attrs 219 if ( $attrs_str != '' ) 220 $attrs_str = ' ' . $attrs_str; 221 222 // Make the element lowercase 223 $el = strtolower($element); 224 225 // Close an opening tag if it had no data 226 if ( $this->first_child ) 227 $this->_add('item', $this->entry_tag, ">"); 228 229 // Add the start tag 230 $this->_add('item', $this->entry_tag, "<$el" . "$attrs_str"); 231 232 // Handle self-closing tags 233 $this->first_child = 1; 234 $this->start_byte = xml_get_current_byte_index($parser); 235 } 236 } 237 238 function character_data($parser, $cdata) { 239 switch ($this->inside_tag) { 240 241 // Grab general channel information 242 case 'FEED': 243 switch ($this->active_tag) { 244 case 'TITLE': 245 $this->_add('channel', strtolower($this->active_tag), $cdata); 246 break; 247 case 'FULLCOUNT': 248 $this->_add('channel', strtolower($this->active_tag), $cdata); 249 break; 250 } 251 break; 252 253 // Grab item information 254 case 'ENTRY': 255 switch ($this->active_tag) { 256 case 'TITLE': 257 $this->_add('item', strtolower($this->active_tag), $cdata); 258 break; 259 260 case 'CONTENT': 261 case 'SUMMARY': 262 $this->_add('item', $this->inside_tag, $cdata); 263 break; 264 265 case 'ISSUED': 266 $this->_add('item', strtolower($this->active_tag), $cdata); 267 break; 268 269 case 'PUBLISHED': 270 $this->_add('item', strtolower($this->active_tag), $cdata); 271 break; 272 273 case 'ID': 274 $this->_add('item', strtolower($this->active_tag), $cdata); 275 break; 276 } 277 break; 278 279 // For author tags 280 case 'ENTRY|AUTHOR': 281 switch ($this->active_tag) { 282 case 'NAME': 283 $this->_add('item', 'author', $cdata); 284 break; 285 case 'URI': 286 $this->_add('item', 'author_uri', $cdata); 287 break; 288 case 'EMAIL': 289 $this->_add('item', 'author_email', $cdata); 290 break; 291 } 292 break; 293 294 } 295 296 // Deal with data inside content tags 297 if ( $this->inside_entry ) { 298 // Append the start tag close if first_child 299 if ( $this->first_child ) { 300 $this->_add('item', $this->entry_tag, '>'); 301 $this->first_child = 0; 302 } 303 304 $this->_add('item', $this->entry_tag, $cdata); 305 } 306 } 307 308 function end_element($parser, $element) { 309 // Deal with tags inside of content and summary 310 if ( $this->inside_entry && $element != 'CONTENT' && $element != 'SUMMARY' ) { 311 $el = strtolower($element); 312 313 // Add the appropriate end tag depending if the element is a self-closing element 314 if ( xml_get_current_byte_index($parser) - $this->start_byte > 2 ) { 315 if ( $this->first_child ) 316 $this->_add('item', $this->entry_tag, ">"); 317 $this->_add('item', $this->entry_tag, "</$el>"); 318 } else { 319 $this->_add('item', $this->entry_tag, " />"); 320 } 321 $this->first_child = 0; 322 } 323 324 // Reset inside tag back to entry for author tags 325 if ( $element == 'AUTHOR' && $this->inside_tag == 'ENTRY|AUTHOR' ) { 326 $this->inside_tag = 'ENTRY'; 327 } 328 329 if ( $element == $this->inside_tag ) { 330 $this->inside_tag = ''; 331 $this->struct[] = array_merge(array('type' => strtolower($element)), $this->last); 332 } 333 334 if ( $element == 'ENTRY' ) { 335 $this->items[] = $this->item; 336 $this->item = ''; 337 } 338 339 if ( $element == 'CONTENT' || $element == 'SUMMARY' ) { 340 $this->inside_entry = 0; 341 } 342 343 $this->active_tag = ''; 344 } 345 346 function _add($type, $field, $value, $array = 0) { 347 if ( $array ) { 348 $this->{$type}[$field][] = $value; 349 } else { 350 if ( empty($this->{$type}) || empty($this->{$type}[$field]) ) { 351 $this->{$type}[$field] = $value; 352 } else { 353 $this->{$type}[$field] .= $value; 354 } 355 } 356 357 $this->last = $this->{$type}; 358 } 359 360 function map_attrs($k, $v) { 361 return "$k=\"$v\""; 362 } 363 364 function get_entries() { 365 // Set the URL 366 if ( $this->next_url != '' ) 367 $url = $this->detect_atom_uri(trim($_POST['mturl'])) . '/offset=' . $this->next_url . '/limit=20'; 368 else 369 $url = $this->detect_atom_uri(trim($_POST['mturl'])) . '/offset=0/limit=20'; 370 371 // Get the entries from the atom feed 372 $result = $this->fetch_atom($url, $this->username, $this->password); 373 if ( $result == 'error' ) return $result; 374 375 // Set the site URL and next URLs 376 $this->site_url = $this->channel['link']; 377 $this->next_url = $this->next_url + 20; 378 // $this->post_count = $this->channel['fullcount']; 379 380 // Convert the parsed atom feed to the posts / pages array 381 foreach ( $this->items AS $item ) { 382 $this->posts[] = $item; 383 } 384 } 385 386 function get_comments($comment_feed) { 387 $comments = array(); 388 389 // While there are more comments 390 do { 391 392 // Select the right comments feed URL 393 if ( $comments_next != '' ) 394 $comment_uri = $comment_feed . '/offset=' . $comments_next . '/limit=20'; 395 else 396 $comment_uri = $comment_feed . '/offset=0/limit=20'; 397 398 // Get the comments from the atom feed 399 if ( !empty($comment_uri) ) 400 $this->fetch_atom($comment_uri, $this->username, $this->password); 401 402 // Convert the parsed atom feed to the comments array 403 foreach ( $this->items AS $item ) { 404 $comments[] = $item; 405 } 406 407 $comments_next = $comments_next + 20; 408 $more_comments = ( count($this->items) > 0 ) ? true : false; 409 410 } while ( $more_comments ); 411 412 return $comments; 413 } 414 415 function get_additional_data($post_id) { 416 include_once(ABSPATH . WPINC . '/class-IXR.php'); 417 418 $rpc = new IXR_Client($this->xmlrpc_url); 419 if ( !$rpc->query('metaWeblog.getPost', $post_id, $this->username, $this->password) ) { 420 // print("Error (" . $rpc->getErrorCode() . "): "); 421 // print($rpc->getErrorMessage() . "\n\n"); 422 } 423 424 return $rpc->getResponse(); 425 } 426 427 function get_trackbacks($post_id) { 428 include_once(ABSPATH . WPINC . '/class-IXR.php'); 429 430 $rpc = new IXR_Client($this->xmlrpc_url); 431 if ( !$rpc->query('mt.getTrackbackPings', $post_id) ) { 432 // print("Error (" . $rpc->getErrorCode() . "): "); 433 // print($rpc->getErrorMessage() . "\n\n"); 434 } 435 436 return $rpc->getResponse(); 437 } 438 439 function check_draft($url) { 440 $ch = curl_init(); 441 curl_setopt($ch, CURLOPT_URL, $url); 442 curl_setopt($ch, CURLOPT_HEADER, 1); 443 curl_setopt($ch, CURLOPT_NOBODY, 1); 444 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 445 curl_setopt($ch, CURLOPT_TIMEOUT, 5); 446 $output = @curl_exec($ch); 447 $response = @curl_getinfo($ch, CURLINFO_HTTP_CODE); 448 449 // If 404 assume this a draft 450 if ( $response == '404' ) 451 return 'draft'; 452 else 453 return 'publish'; 454 } 455 456 function process_posts() { 457 // Start the output list if this is the first run 458 if ( $this->first_run == true ) { 459 echo '<div class="wrap"><ol>'; 460 $this->first_run = false; 461 } 462 463 // Use output buffering to show the current import progress 464 if (ob_get_level() == 0) { 465 ob_start(); 466 } 467 // Pad the page with some data so output buffering works across browsers 468 echo str_pad('',4096); 469 470 foreach ( $this->posts AS $entry ) { 471 472 // Construct the post class from the AtomPub feed 473 $post = new StdClass(); 474 475 // The entry type (over 1 and we have a post) 476 if ( count(explode('/', substr($entry['link'], strlen($this->site_url)))) > 1 ) 477 $post->post_type = 'post'; 478 else 479 $post->post_type = 'page'; 480 // The entry title 481 if ( isset($entry['title']) ) 482 $post->post_title = trim($entry['title']); 483 else 484 $post->post_title = ''; 485 // The post content 486 if ( isset($entry['content']) ) 487 $post->post_content = trim($entry['content']); 488 // The post excerpt 489 if ( isset($entry['summary']) ) 490 $post->post_excerpt = trim($entry['summary']); 491 // The post date 492 $date = strtotime(trim($entry['issued'])); 493 $date = date('Y-m-d H:i:s', $date); 494 $date_gmt = get_gmt_from_date($date); 495 $post->post_modified = $date; 496 $post->post_modified_gmt = $date_gmt; 497 $post->post_date = $date; 498 $post->post_date_gmt = $date_gmt; 499 // The post slug 500 $slug = array_reverse(explode('/', trim($entry['link']))); 501 $slug = substr($slug[0], 0 , -5); 502 $post->post_name = $slug; 503 // The categories and tags 504 if ( isset($entry['categories']) ) { 505 foreach ( $entry['categories'] AS $cat ) { 506 $post->categories[] = trim($cat['term']); 507 $post_keywords = ''; 508 } 509 } else { 510 $post->categories = array(); 511 $post->post_keywords = ''; 512 } 513 // The post author 514 if ( isset($entry['author']) ) 515 $post->post_author = trim($entry['author']); 516 // The post id 517 $post_id = trim($entry['id']); 518 $post_id = substr(strrchr($post_id, '.'), 1); 519 // The additional data 520 $additional_data = $this->get_additional_data($post_id); 521 // The comment status 522 if ( $additional_data['mt_allow_comments'] == 1 ) 523 $post->comment_status = 'open'; 524 else 525 $post->comment_status = 'closed'; 526 if ( $additional_data['mt_allow_pings'] == 1 ) 527 $post->ping_status = 'open'; 528 else 529 $post->ping_status = 'closed'; 530 // The tags 531 $post->post_keywords = $additional_data['mt_tags']; 532 // The draft status 533 $post->post_status = $this->check_draft(trim($entry['link'])); 534 535 // Set the post count if we weren't able to determine it earlier. Asssume the current ID count is the number of posts 536 if ( $this->post_count == '' ) 537 $this->post_count = $post_id; 538 539 unset($comments); 540 $comments = array(); 541 542 // Fetch the comments if the post has comments 543 $coms = $this->get_comments(trim($entry['link:replies'])); 544 545 if ( count($coms) > 0 ) { 546 foreach ( $coms AS $com ) { 547 // Construct the comment class from the AtomPub feed 548 $comment = new StdClass(); 549 550 // The comment date 551 $date = strtotime(trim($com['published'])); 552 $date = date('Y-m-d H:i:s', $date); 553 $comment->comment_date = $date; 554 // The comment content 555 $comment->comment_content = trim($com['content']); 556 // The comment author 557 $comment->comment_author = trim($com['author']); 558 // The comment author uri 559 $comment->comment_author_url = trim($com['author_uri']); 560 // The comment author email 561 $comment->comment_author_email = trim($com['author_email']); 562 // Set the unknown variables 563 $comment->comment_author_IP = ''; 564 565 // Add the comment to the comments array 566 $comments[] = $comment; 567 } 568 } 569 570 unset($trackbacks); 571 $pings = array(); 572 573 // Fetch the trackbacks 574 $trackbacks = $this->get_trackbacks($post_id); 575 if ( count($trackbacks) > 0 ) { 576 foreach ( $trackbacks AS $trackback ) { 577 $ping = new StdClass(); 578 // The ping date (the date of the post since we don't know the real one) 579 $ping->comment_date = $post->post_date; 580 // The ping IP address 581 $ping->comment_author_IP = $trackback['pingIP']; 582 // The ping URL 583 $ping->comment_author_url = $trackback['pingURL']; 584 // The ping author 585 $ping->comment_author = $trackback['pingTitle']; 586 // Set the unknow variables 587 $ping->comment_author_email = ''; 588 $ping->comment_content = ''; 589 $ping->comment_type = 'pingback'; 590 591 // Add the ping to the pings array 592 $pings[] = $ping; 593 } 594 } 595 596 $result = $this->save_post($post, $comments, $pings); 597 if ( is_wp_error( $result ) ) 598 return $result; 599 600 // Increase the post count 601 $this->current_post++; 602 603 // Update the progress bar 604 $progress_bar = round(( $this->current_post / $this->post_count ) * 500); 605 $progress_percent = round(( $this->current_post / $this->post_count ) * 100, 1); 606 ?> 607 <script type="text/javascript"> 608 jQuery('.progress_bar .progress_display').animate({ 609 width: "<?php echo $progress_bar; ?>px" 610 }, 200); 611 jQuery('.progress_percent span.percent').html('<?php echo $progress_percent; ?>'); 612 <?php if ( $progress_percent == 100 ) { ?> 613 jQuery('.progress_percent img').hide(); 614 <?php } ?> 615 </script> 616 <?php 617 618 // Flush the output buffer to show current state 619 ob_flush(); 620 flush(); 621 usleep(50000); 622 } 623 624 // Clean up after using the output buffer 625 ob_end_flush(); 626 627 // If there are no more posts end the list tags 628 if ( $this->more_posts == false ) { 629 630 // Update the progress bar 631 $progress_bar = 500; 632 $progress_percent = 100; 633 ?> 634 <script type="text/javascript"> 635 jQuery('.progress_bar .progress_display').animate({ 636 width: "<?php echo $progress_bar; ?>px" 637 }, 200); 638 jQuery('.progress_percent span.percent').html('<?php echo $progress_percent; ?>'); 639 <?php if ( $progress_percent == 100 ) { ?> 640 jQuery('.progress_percent img').hide(); 641 <?php } ?> 642 </script> 643 <?php 644 645 echo '</ol>'; 646 do_action('import_done', 'mt-atom'); 647 echo '<h3>'.sprintf(__('All done. <a href="%s">Have fun!</a>'), get_option('home')).'</h3></div>'; 648 } 649 } 650 651 function save_post(&$post, &$comments, &$pings) { 652 $post = get_object_vars($post); 653 $post = add_magic_quotes($post); 654 $post = (object) $post; 655 656 if ( $post_id = post_exists($post->post_title, '', $post->post_date) ) { 657 echo '<li>'; 658 printf(__('Post <em>%s</em> already exists.'), stripslashes($post->post_title)); 659 } else { 660 echo '<li>'; 661 printf(__('Importing post <em>%s</em>...'), stripslashes($post->post_title)); 662 663 $post->post_author = $this->check_author($post->post_author); //just so that if a post already exists, new users are not created by checkauthor 664 $post_id = wp_insert_post($post); 665 if ( is_wp_error( $post_id ) ) 666 return $post_id; 667 668 // Add categories. 669 if ( 0 != count($post->categories) ) { 670 wp_create_categories($post->categories, $post_id); 671 } 672 673 // Add tags or keywords 674 if ( 1 < strlen($post->post_keywords) ) { 675 // Keywords exist. 676 printf(__('<br />Adding tags <i>%s</i>...'), stripslashes($post->post_keywords)); 677 wp_add_post_tags($post_id, $post->post_keywords); 678 } 679 680 $num_comments = 0; 681 foreach ( $comments as $comment ) { 682 $comment = get_object_vars($comment); 683 $comment = add_magic_quotes($comment); 684 685 if ( !comment_exists($comment['comment_author'], $comment['comment_date'])) { 686 $comment['comment_post_ID'] = $post_id; 687 $comment = wp_filter_comment($comment); 688 wp_insert_comment($comment); 689 $num_comments++; 690 } 691 } 692 693 if ( $num_comments ) 694 printf(' '.__ngettext('(%s comment)', '(%s comments)', $num_comments), $num_comments); 695 696 $num_pings = 0; 697 foreach ( $pings as $ping ) { 698 $ping = get_object_vars($ping); 699 $ping = add_magic_quotes($ping); 700 701 if ( !comment_exists($ping['comment_author'], $ping['comment_date'])) { 702 $ping['comment_content'] = "<strong>{$ping['comment_author']}</strong>"; 703 $ping['comment_post_ID'] = $post_id; 704 $ping = wp_filter_comment($ping); 705 wp_insert_comment($ping); 706 $num_pings++; 707 } 708 } 709 710 if ( $num_pings ) 711 printf(' '.__ngettext('(%s ping)', '(%s pings)', $num_pings), $num_pings); 712 } 713 714 echo '</li>'; 715 } 716 717 function check_author($author) { 718 global $wpdb; 719 720 $user_id = username_exists($author); 721 722 // If the username does not exist 723 if ( !$user_id ) { 724 $pass = wp_generate_password(); 725 $user_id = wp_create_user($author, $pass); 726 } 727 728 return $user_id; 729 } 730 731 function error() { 732 ?> 733 <div class="narrow"> 734 <h3><?php _e('Import Failed'); ?></h3> 735 <p><?php _e('There was an error starting the import process. Please go back and verify that the URL, username, and password were entered correctly.'); ?></p> 736 <p><a href="<?php echo add_query_arg('step', 0); ?>"><?php _e('Restart the Import Process »')?></a></p> 737 </div> 738 <?php 739 } 740 741 function import() { 742 $this->header(); 743 744 // Add the username and password as a global 745 $this->username = $_POST['mtuser']; 746 $this->password = $_POST['mtpass']; 747 748 // Use output buffering so the progress bar appears immediately 749 if (ob_get_level() == 0) { 750 ob_start(); 751 } 752 753 // Pad the page with some data so output buffering works across browsers 754 echo str_pad('',4096); 755 756 ?> 757 <h3><?php _e('Import in Progress, Results Will Appear Momentarily'); ?></h3> 758 759 <p><?php _e('While the importer is running do not navigate away from this page.'); ?></p> 760 761 <div class="progress_bar" style="float:left; width: 500px; height; 20px; padding: 1px; border: 1px solid #dadada;"> 762 <div class="progress_display" style="width: 0px; background: #ccc"> </div> 763 </div> 764 <div class="progress_percent" style="padding: 2px;"> <img src="<?php echo get_option('home'); ?>/wp-admin/images/loading.gif" alt="Loading" style="margin-bottom: -3px;" /> <span class="percent">0</span>%</div> 765 <?php 766 767 // Clear the output buffer 768 ob_flush(); 769 flush(); 770 ob_end_flush(); 771 772 // While we have more posts 773 do { 774 775 $result = $this->get_entries(); 776 777 $this->more_posts = ( count($this->posts) > 0 ) ? true : false; 778 779 if ( $result != 'error' ) 780 $result = $this->process_posts(); 781 782 if ( is_wp_error( $result ) ) 783 echo $result->get_error_message(); 784 785 // Free memory for next round of post 786 unset($this->posts); 787 $this->posts = array(); 788 789 } while ( $this->more_posts ); 790 791 $this->footer(); 792 } 793 794 function dispatch() { 795 if (empty ($_GET['step'])) 796 $step = 0; 797 else 798 $step = (int) $_GET['step']; 799 800 switch ($step) { 801 case 0 : 802 $this->greet(); 803 break; 804 case 1 : 805 check_admin_referer('import-mt-atom'); 806 $this->import(); 807 break; 808 } 809 } 810 811 function MT_Atom_Import() { 812 // Nothing. 813 } 814 } 815 816 $mt_atom_import = new MT_Atom_Import(); 817 818 register_importer('mt-atom', __('Movable Type'), __('Import posts and comments from a Movable Type blog version 4.2 or newer.'), array ($mt_atom_import, 'dispatch')); 819 ?> 820 No newline at end of file -
wp-admin/import/mt.php
24 24 25 25 function header() { 26 26 echo '<div class="wrap">'; 27 echo '<h2>'.__('Import Movable Type or TypePad').'</h2>';27 echo '<h2>'.__('Import Movable Type Old').'</h2>'; 28 28 } 29 29 30 30 function footer() { … … 35 35 $this->header(); 36 36 ?> 37 37 <div class="narrow"> 38 <p><?php _e('Howdy! We’re about to begin importing all of your Movable Type or Typepadentries into WordPress. To begin, either choose a file to upload and click "Upload file and import," or use FTP to upload your MT export file as <code>mt-export.txt</code> in your <code>/wp-content/</code> directory and then click "Import mt-export.txt"'); ?></p>38 <p><?php _e('Howdy! We’re about to begin importing all of your Movable Type entries into WordPress. To begin, either choose a file to upload and click "Upload file and import," or use FTP to upload your MT export file as <code>mt-export.txt</code> in your <code>/wp-content/</code> directory and then click "Import mt-export.txt"'); ?></p> 39 39 40 40 <?php wp_import_upload_form( add_query_arg('step', 1) ); ?> 41 41 <form method="post" action="<?php echo add_query_arg('step', 1); ?>" class="import-upload-form"> … … 478 478 479 479 $mt_import = new MT_Import(); 480 480 481 register_importer('mt', __('Movable Type and TypePad'), __('Import posts and comments from a Movable Type or Typepad blog.'), array ($mt_import, 'dispatch'));481 register_importer('mt', __('Movable Type Old'), __('Import posts and comments from a Movable Type blog version 4.12 or older.'), array ($mt_import, 'dispatch')); 482 482 ?>