-
+ 4B8F4FA9B2B81709D307FB19FF5BB38006A93B29F9ABD31A6B13026DB2AAFFBECC26F6E94101315EF8E62FFF8EF600C85690B87B20B0D481757DD6760D251BDEmp-wp/wp-admin/import/blogger.php(0 . 0)(1 . 1061)
16315 <?php
16316 /**
16317 * Blogger Importer
16318 *
16319 * @package WordPress
16320 * @subpackage Importer
16321 */
16322
16323 /**
16324 * How many records per GData query
16325 *
16326 * @package WordPress
16327 * @subpackage Blogger_Import
16328 * @var int
16329 * @since unknown
16330 */
16331 define( 'MAX_RESULTS', 50 );
16332
16333 /**
16334 * How many seconds to let the script run
16335 *
16336 * @package WordPress
16337 * @subpackage Blogger_Import
16338 * @var int
16339 * @since unknown
16340 */
16341 define( 'MAX_EXECUTION_TIME', 20 );
16342
16343 /**
16344 * How many seconds between status bar updates
16345 *
16346 * @package WordPress
16347 * @subpackage Blogger_Import
16348 * @var int
16349 * @since unknown
16350 */
16351 define( 'STATUS_INTERVAL', 3 );
16352
16353 /**
16354 * Blogger Importer class
16355 *
16356 * @since unknown
16357 */
16358 class Blogger_Import {
16359
16360 // Shows the welcome screen and the magic auth link.
16361 function greet() {
16362 $next_url = get_option('siteurl') . '/wp-admin/index.php?import=blogger&noheader=true';
16363 $auth_url = "https://www.google.com/accounts/AuthSubRequest";
16364 $title = __('Import Blogger');
16365 $welcome = __('Howdy! This importer allows you to import posts and comments from your Blogger account into your WordPress blog.');
16366 $prereqs = __('To use this importer, you must have a Google account and an upgraded (New, was Beta) blog hosted on blogspot.com or a custom domain (not FTP).');
16367 $stepone = __('The first thing you need to do is tell Blogger to let WordPress access your account. You will be sent back here after providing authorization.');
16368 $auth = __('Authorize');
16369
16370 echo "
16371 <div class='wrap'>
16372 ".screen_icon()."
16373 <h2>$title</h2>
16374 <p>$welcome</p><p>$prereqs</p><p>$stepone</p>
16375 <form action='$auth_url' method='get'>
16376 <p class='submit' style='text-align:left;'>
16377 <input type='submit' class='button' value='$auth' />
16378 <input type='hidden' name='scope' value='http://www.blogger.com/feeds/' />
16379 <input type='hidden' name='session' value='1' />
16380 <input type='hidden' name='secure' value='0' />
16381 <input type='hidden' name='next' value='$next_url' />
16382 </p>
16383 </form>
16384 </div>\n";
16385 }
16386
16387 function uh_oh($title, $message, $info) {
16388 echo "<div class='wrap'>";
16389 screen_icon();
16390 echo "<h2>$title</h2><p>$message</p><pre>$info</pre></div>";
16391 }
16392
16393 function auth() {
16394 // We have a single-use token that must be upgraded to a session token.
16395 $token = preg_replace( '/[^-_0-9a-zA-Z]/', '', $_GET['token'] );
16396 $headers = array(
16397 "GET /accounts/AuthSubSessionToken HTTP/1.0",
16398 "Authorization: AuthSub token=\"$token\""
16399 );
16400 $request = join( "\r\n", $headers ) . "\r\n\r\n";
16401 $sock = $this->_get_auth_sock( );
16402 if ( ! $sock ) return false;
16403 $response = $this->_txrx( $sock, $request );
16404 preg_match( '/token=([-_0-9a-z]+)/i', $response, $matches );
16405 if ( empty( $matches[1] ) ) {
16406 $this->uh_oh(
16407 __( 'Authorization failed' ),
16408 __( 'Something went wrong. If the problem persists, send this info to support:' ),
16409 htmlspecialchars($response)
16410 );
16411 return false;
16412 }
16413 $this->token = $matches[1];
16414
16415 wp_redirect( remove_query_arg( array( 'token', 'noheader' ) ) );
16416 }
16417
16418 function get_token_info() {
16419 $headers = array(
16420 "GET /accounts/AuthSubTokenInfo HTTP/1.0",
16421 "Authorization: AuthSub token=\"$this->token\""
16422 );
16423 $request = join( "\r\n", $headers ) . "\r\n\r\n";
16424 $sock = $this->_get_auth_sock( );
16425 if ( ! $sock ) return;
16426 $response = $this->_txrx( $sock, $request );
16427 return $this->parse_response($response);
16428 }
16429
16430 function token_is_valid() {
16431 $info = $this->get_token_info();
16432
16433 if ( $info['code'] == 200 )
16434 return true;
16435
16436 return false;
16437 }
16438
16439 function show_blogs($iter = 0) {
16440 if ( empty($this->blogs) ) {
16441 $headers = array(
16442 "GET /feeds/default/blogs HTTP/1.0",
16443 "Host: www.blogger.com",
16444 "Authorization: AuthSub token=\"$this->token\""
16445 );
16446 $request = join( "\r\n", $headers ) . "\r\n\r\n";
16447 $sock = $this->_get_blogger_sock( );
16448 if ( ! $sock ) return;
16449 $response = $this->_txrx( $sock, $request );
16450
16451 // Quick and dirty XML mining.
16452 list( $headers, $xml ) = explode( "\r\n\r\n", $response );
16453 $p = xml_parser_create();
16454 xml_parse_into_struct($p, $xml, $vals, $index);
16455 xml_parser_free($p);
16456
16457 $this->title = $vals[$index['TITLE'][0]]['value'];
16458
16459 // Give it a few retries... this step often flakes out the first time.
16460 if ( empty( $index['ENTRY'] ) ) {
16461 if ( $iter < 3 ) {
16462 return $this->show_blogs($iter + 1);
16463 } else {
16464 $this->uh_oh(
16465 __('Trouble signing in'),
16466 __('We were not able to gain access to your account. Try starting over.'),
16467 ''
16468 );
16469 return false;
16470 }
16471 }
16472
16473 foreach ( $index['ENTRY'] as $i ) {
16474 $blog = array();
16475 while ( ( $tag = $vals[$i] ) && ! ( $tag['tag'] == 'ENTRY' && $tag['type'] == 'close' ) ) {
16476 if ( $tag['tag'] == 'TITLE' ) {
16477 $blog['title'] = $tag['value'];
16478 } elseif ( $tag['tag'] == 'SUMMARY' ) {
16479 $blog['summary'] == $tag['value'];
16480 } elseif ( $tag['tag'] == 'LINK' ) {
16481 if ( $tag['attributes']['REL'] == 'alternate' && $tag['attributes']['TYPE'] == 'text/html' ) {
16482 $parts = parse_url( $tag['attributes']['HREF'] );
16483 $blog['host'] = $parts['host'];
16484 } elseif ( $tag['attributes']['REL'] == 'edit' )
16485 $blog['gateway'] = $tag['attributes']['HREF'];
16486 }
16487 ++$i;
16488 }
16489 if ( ! empty ( $blog ) ) {
16490 $blog['total_posts'] = $this->get_total_results('posts', $blog['host']);
16491 $blog['total_comments'] = $this->get_total_results('comments', $blog['host']);
16492 $blog['mode'] = 'init';
16493 $this->blogs[] = $blog;
16494 }
16495 }
16496
16497 if ( empty( $this->blogs ) ) {
16498 $this->uh_oh(
16499 __('No blogs found'),
16500 __('We were able to log in but there were no blogs. Try a different account next time.'),
16501 ''
16502 );
16503 return false;
16504 }
16505 }
16506 //echo '<pre>'.print_r($this,1).'</pre>';
16507 $start = js_escape( __('Import') );
16508 $continue = js_escape( __('Continue') );
16509 $stop = js_escape( __('Importing...') );
16510 $authors = js_escape( __('Set Authors') );
16511 $loadauth = js_escape( __('Preparing author mapping form...') );
16512 $authhead = js_escape( __('Final Step: Author Mapping') );
16513 $nothing = js_escape( __('Nothing was imported. Had you already imported this blog?') );
16514 $title = __('Blogger Blogs');
16515 $name = __('Blog Name');
16516 $url = __('Blog URL');
16517 $action = __('The Magic Button');
16518 $posts = __('Posts');
16519 $comments = __('Comments');
16520 $noscript = __('This feature requires Javascript but it seems to be disabled. Please enable Javascript and then reload this page. Don\'t worry, you can turn it back off when you\'re done.');
16521
16522 $interval = STATUS_INTERVAL * 1000;
16523
16524 foreach ( $this->blogs as $i => $blog ) {
16525 if ( $blog['mode'] == 'init' )
16526 $value = $start;
16527 elseif ( $blog['mode'] == 'posts' || $blog['mode'] == 'comments' )
16528 $value = $continue;
16529 else
16530 $value = $authors;
16531 $blogtitle = js_escape( $blog['title'] );
16532 $pdone = isset($blog['posts_done']) ? (int) $blog['posts_done'] : 0;
16533 $cdone = isset($blog['comments_done']) ? (int) $blog['comments_done'] : 0;
16534 $init .= "blogs[$i]=new blog($i,'$blogtitle','{$blog['mode']}'," . $this->get_js_status($i) . ');';
16535 $pstat = "<div class='ind' id='pind$i'> </div><div id='pstat$i' class='stat'>$pdone/{$blog['total_posts']}</div>";
16536 $cstat = "<div class='ind' id='cind$i'> </div><div id='cstat$i' class='stat'>$cdone/{$blog['total_comments']}</div>";
16537 $rows .= "<tr id='blog$i'><td class='blogtitle'>$blogtitle</td><td class='bloghost'>{$blog['host']}</td><td class='bar'>$pstat</td><td class='bar'>$cstat</td><td class='submit'><input type='submit' class='button' id='submit$i' value='$value' /><input type='hidden' name='blog' value='$i' /></td></tr>\n";
16538 }
16539
16540 echo "<div class='wrap'><h2>$title</h2><noscript>$noscript</noscript><table cellpadding='5px'><thead><tr><td>$name</td><td>$url</td><td>$posts</td><td>$comments</td><td>$action</td></tr></thead>\n$rows</table></div>";
16541 echo "
16542 <script type='text/javascript'>
16543 /* <![CDATA[ */
16544 var strings = {cont:'$continue',stop:'$stop',stopping:'$stopping',authors:'$authors',nothing:'$nothing'};
16545 var blogs = {};
16546 function blog(i, title, mode, status){
16547 this.blog = i;
16548 this.mode = mode;
16549 this.title = title;
16550 this.status = status;
16551 this.button = document.getElementById('submit'+this.blog);
16552 };
16553 blog.prototype = {
16554 start: function() {
16555 this.cont = true;
16556 this.kick();
16557 this.check();
16558 },
16559 kick: function() {
16560 ++this.kicks;
16561 var i = this.blog;
16562 jQuery.post('admin.php?import=blogger&noheader=true',{blog:this.blog},function(text,result){blogs[i].kickd(text,result)});
16563 },
16564 check: function() {
16565 ++this.checks;
16566 var i = this.blog;
16567 jQuery.post('admin.php?import=blogger&noheader=true&status=true',{blog:this.blog},function(text,result){blogs[i].checkd(text,result)});
16568 },
16569 kickd: function(text, result) {
16570 if ( result == 'error' ) {
16571 // TODO: exception handling
16572 if ( this.cont )
16573 setTimeout('blogs['+this.blog+'].kick()', 1000);
16574 } else {
16575 if ( text == 'done' ) {
16576 this.stop();
16577 this.done();
16578 } else if ( text == 'nothing' ) {
16579 this.stop();
16580 this.nothing();
16581 } else if ( text == 'continue' ) {
16582 this.kick();
16583 } else if ( this.mode = 'stopped' )
16584 jQuery(this.button).attr('value', strings.cont);
16585 }
16586 --this.kicks;
16587 },
16588 checkd: function(text, result) {
16589 if ( result == 'error' ) {
16590 // TODO: exception handling
16591 } else {
16592 eval('this.status='+text);
16593 jQuery('#pstat'+this.blog).empty().append(this.status.p1+'/'+this.status.p2);
16594 jQuery('#cstat'+this.blog).empty().append(this.status.c1+'/'+this.status.c2);
16595 this.update();
16596 if ( this.cont || this.kicks > 0 )
16597 setTimeout('blogs['+this.blog+'].check()', $interval);
16598 }
16599 --this.checks;
16600 },
16601 update: function() {
16602 jQuery('#pind'+this.blog).width(((this.status.p1>0&&this.status.p2>0)?(this.status.p1/this.status.p2*jQuery('#pind'+this.blog).parent().width()):1)+'px');
16603 jQuery('#cind'+this.blog).width(((this.status.c1>0&&this.status.c2>0)?(this.status.c1/this.status.c2*jQuery('#cind'+this.blog).parent().width()):1)+'px');
16604 },
16605 stop: function() {
16606 this.cont = false;
16607 },
16608 done: function() {
16609 this.mode = 'authors';
16610 jQuery(this.button).attr('value', strings.authors);
16611 },
16612 nothing: function() {
16613 this.mode = 'nothing';
16614 jQuery(this.button).remove();
16615 alert(strings.nothing);
16616 },
16617 getauthors: function() {
16618 if ( jQuery('div.wrap').length > 1 )
16619 jQuery('div.wrap').gt(0).remove();
16620 jQuery('div.wrap').empty().append('<h2>$authhead</h2><h3>' + this.title + '</h3>');
16621 jQuery('div.wrap').append('<p id=\"auth\">$loadauth</p>');
16622 jQuery('p#auth').load('index.php?import=blogger&noheader=true&authors=1',{blog:this.blog});
16623 },
16624 init: function() {
16625 this.update();
16626 var i = this.blog;
16627 jQuery(this.button).bind('click', function(){return blogs[i].click();});
16628 this.kicks = 0;
16629 this.checks = 0;
16630 },
16631 click: function() {
16632 if ( this.mode == 'init' || this.mode == 'stopped' || this.mode == 'posts' || this.mode == 'comments' ) {
16633 this.mode = 'started';
16634 this.start();
16635 jQuery(this.button).attr('value', strings.stop);
16636 } else if ( this.mode == 'started' ) {
16637 return false; // let it run...
16638 this.mode = 'stopped';
16639 this.stop();
16640 if ( this.checks > 0 || this.kicks > 0 ) {
16641 this.mode = 'stopping';
16642 jQuery(this.button).attr('value', strings.stopping);
16643 } else {
16644 jQuery(this.button).attr('value', strings.cont);
16645 }
16646 } else if ( this.mode == 'authors' ) {
16647 document.location = 'index.php?import=blogger&authors=1&blog='+this.blog;
16648 //this.mode = 'authors2';
16649 //this.getauthors();
16650 }
16651 return false;
16652 }
16653 };
16654 $init
16655 jQuery.each(blogs, function(i, me){me.init();});
16656 /* ]]> */
16657 </script>\n";
16658 }
16659
16660 // Handy function for stopping the script after a number of seconds.
16661 function have_time() {
16662 global $importer_started;
16663 if ( time() - $importer_started > MAX_EXECUTION_TIME )
16664 die('continue');
16665 return true;
16666 }
16667
16668 function get_total_results($type, $host) {
16669 $headers = array(
16670 "GET /feeds/$type/default?max-results=1&start-index=2 HTTP/1.0",
16671 "Host: $host",
16672 "Authorization: AuthSub token=\"$this->token\""
16673 );
16674 $request = join( "\r\n", $headers ) . "\r\n\r\n";
16675 $sock = $this->_get_blogger_sock( $host );
16676 if ( ! $sock ) return;
16677 $response = $this->_txrx( $sock, $request );
16678 $response = $this->parse_response( $response );
16679 $parser = xml_parser_create();
16680 xml_parse_into_struct($parser, $response['body'], $struct, $index);
16681 xml_parser_free($parser);
16682 $total_results = $struct[$index['OPENSEARCH:TOTALRESULTS'][0]]['value'];
16683 return (int) $total_results;
16684 }
16685
16686 function import_blog($blogID) {
16687 global $importing_blog;
16688 $importing_blog = $blogID;
16689
16690 if ( isset($_GET['authors']) )
16691 return print($this->get_author_form());
16692
16693 header('Content-Type: text/plain');
16694
16695 if ( isset($_GET['status']) )
16696 die($this->get_js_status());
16697
16698 if ( isset($_GET['saveauthors']) )
16699 die($this->save_authors());
16700
16701 $blog = $this->blogs[$blogID];
16702 $total_results = $this->get_total_results('posts', $blog['host']);
16703 $this->blogs[$importing_blog]['total_posts'] = $total_results;
16704
16705 $start_index = $total_results - MAX_RESULTS + 1;
16706
16707 if ( isset( $this->blogs[$importing_blog]['posts_start_index'] ) )
16708 $start_index = (int) $this->blogs[$importing_blog]['posts_start_index'];
16709 elseif ( $total_results > MAX_RESULTS )
16710 $start_index = $total_results - MAX_RESULTS + 1;
16711 else
16712 $start_index = 1;
16713
16714 // This will be positive until we have finished importing posts
16715 if ( $start_index > 0 ) {
16716 // Grab all the posts
16717 $this->blogs[$importing_blog]['mode'] = 'posts';
16718 $query = "start-index=$start_index&max-results=" . MAX_RESULTS;
16719 do {
16720 $index = $struct = $entries = array();
16721 $headers = array(
16722 "GET /feeds/posts/default?$query HTTP/1.0",
16723 "Host: {$blog['host']}",
16724 "Authorization: AuthSub token=\"$this->token\""
16725 );
16726 $request = join( "\r\n", $headers ) . "\r\n\r\n";
16727 $sock = $this->_get_blogger_sock( $blog['host'] );
16728 if ( ! $sock ) return; // TODO: Error handling
16729 $response = $this->_txrx( $sock, $request );
16730
16731 $response = $this->parse_response( $response );
16732
16733 // Extract the entries and send for insertion
16734 preg_match_all( '/<entry[^>]*>.*?<\/entry>/s', $response['body'], $matches );
16735 if ( count( $matches[0] ) ) {
16736 $entries = array_reverse($matches[0]);
16737 foreach ( $entries as $entry ) {
16738 $entry = "<feed>$entry</feed>";
16739 $AtomParser = new AtomParser();
16740 $AtomParser->parse( $entry );
16741 $result = $this->import_post($AtomParser->entry);
16742 if ( is_wp_error( $result ) )
16743 return $result;
16744 unset($AtomParser);
16745 }
16746 } else break;
16747
16748 // Get the 'previous' query string which we'll use on the next iteration
16749 $query = '';
16750 $links = preg_match_all('/<link([^>]*)>/', $response['body'], $matches);
16751 if ( count( $matches[1] ) )
16752 foreach ( $matches[1] as $match )
16753 if ( preg_match('/rel=.previous./', $match) )
16754 $query = html_entity_decode( preg_replace('/^.*href=[\'"].*\?(.+)[\'"].*$/', '$1', $match) );
16755
16756 if ( $query ) {
16757 parse_str($query, $q);
16758 $this->blogs[$importing_blog]['posts_start_index'] = (int) $q['start-index'];
16759 } else
16760 $this->blogs[$importing_blog]['posts_start_index'] = 0;
16761 $this->save_vars();
16762 } while ( !empty( $query ) && $this->have_time() );
16763 }
16764
16765 $total_results = $this->get_total_results( 'comments', $blog['host'] );
16766 $this->blogs[$importing_blog]['total_comments'] = $total_results;
16767
16768 if ( isset( $this->blogs[$importing_blog]['comments_start_index'] ) )
16769 $start_index = (int) $this->blogs[$importing_blog]['comments_start_index'];
16770 elseif ( $total_results > MAX_RESULTS )
16771 $start_index = $total_results - MAX_RESULTS + 1;
16772 else
16773 $start_index = 1;
16774
16775 if ( $start_index > 0 ) {
16776 // Grab all the comments
16777 $this->blogs[$importing_blog]['mode'] = 'comments';
16778 $query = "start-index=$start_index&max-results=" . MAX_RESULTS;
16779 do {
16780 $index = $struct = $entries = array();
16781 $headers = array(
16782 "GET /feeds/comments/default?$query HTTP/1.0",
16783 "Host: {$blog['host']}",
16784 "Authorization: AuthSub token=\"$this->token\""
16785 );
16786 $request = join( "\r\n", $headers ) . "\r\n\r\n";
16787 $sock = $this->_get_blogger_sock( $blog['host'] );
16788 if ( ! $sock ) return; // TODO: Error handling
16789 $response = $this->_txrx( $sock, $request );
16790
16791 $response = $this->parse_response( $response );
16792
16793 // Extract the comments and send for insertion
16794 preg_match_all( '/<entry[^>]*>.*?<\/entry>/s', $response['body'], $matches );
16795 if ( count( $matches[0] ) ) {
16796 $entries = array_reverse( $matches[0] );
16797 foreach ( $entries as $entry ) {
16798 $entry = "<feed>$entry</feed>";
16799 $AtomParser = new AtomParser();
16800 $AtomParser->parse( $entry );
16801 $this->import_comment($AtomParser->entry);
16802 unset($AtomParser);
16803 }
16804 }
16805
16806 // Get the 'previous' query string which we'll use on the next iteration
16807 $query = '';
16808 $links = preg_match_all('/<link([^>]*)>/', $response['body'], $matches);
16809 if ( count( $matches[1] ) )
16810 foreach ( $matches[1] as $match )
16811 if ( preg_match('/rel=.previous./', $match) )
16812 $query = html_entity_decode( preg_replace('/^.*href=[\'"].*\?(.+)[\'"].*$/', '$1', $match) );
16813
16814 parse_str($query, $q);
16815
16816 $this->blogs[$importing_blog]['comments_start_index'] = (int) $q['start-index'];
16817 $this->save_vars();
16818 } while ( !empty( $query ) && $this->have_time() );
16819 }
16820 $this->blogs[$importing_blog]['mode'] = 'authors';
16821 $this->save_vars();
16822 if ( !$this->blogs[$importing_blog]['posts_done'] && !$this->blogs[$importing_blog]['comments_done'] )
16823 die('nothing');
16824 do_action('import_done', 'blogger');
16825 die('done');
16826 }
16827
16828 function convert_date( $date ) {
16829 preg_match('#([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(?:\.[0-9]+)?(Z|[\+|\-][0-9]{2,4}){0,1}#', $date, $date_bits);
16830 $offset = iso8601_timezone_to_offset( $date_bits[7] );
16831 $timestamp = gmmktime($date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1]);
16832 $timestamp -= $offset; // Convert from Blogger local time to GMT
16833 $timestamp += get_option('gmt_offset') * 3600; // Convert from GMT to WP local time
16834 return gmdate('Y-m-d H:i:s', $timestamp);
16835 }
16836
16837 function no_apos( $string ) {
16838 return str_replace( ''', "'", $string);
16839 }
16840
16841 function min_whitespace( $string ) {
16842 return preg_replace( '|\s+|', ' ', $string );
16843 }
16844
16845 function import_post( $entry ) {
16846 global $importing_blog;
16847
16848 // The old permalink is all Blogger gives us to link comments to their posts.
16849 if ( isset( $entry->draft ) )
16850 $rel = 'self';
16851 else
16852 $rel = 'alternate';
16853 foreach ( $entry->links as $link ) {
16854 if ( $link['rel'] == $rel ) {
16855 $parts = parse_url( $link['href'] );
16856 $entry->old_permalink = $parts['path'];
16857 break;
16858 }
16859 }
16860
16861 $post_date = $this->convert_date( $entry->published );
16862 $post_content = trim( addslashes( $this->no_apos( html_entity_decode( $entry->content ) ) ) );
16863 $post_title = trim( addslashes( $this->no_apos( $this->min_whitespace( $entry->title ) ) ) );
16864 $post_status = isset( $entry->draft ) ? 'draft' : 'publish';
16865
16866 // Clean up content
16867 $post_content = preg_replace('|<(/?[A-Z]+)|e', "'<' . strtolower('$1')", $post_content);
16868 $post_content = str_replace('<br>', '<br />', $post_content);
16869 $post_content = str_replace('<hr>', '<hr />', $post_content);
16870
16871 // Checks for duplicates
16872 if ( isset( $this->blogs[$importing_blog]['posts'][$entry->old_permalink] ) ) {
16873 ++$this->blogs[$importing_blog]['posts_skipped'];
16874 } elseif ( $post_id = post_exists( $post_title, $post_content, $post_date ) ) {
16875 $this->blogs[$importing_blog]['posts'][$entry->old_permalink] = $post_id;
16876 ++$this->blogs[$importing_blog]['posts_skipped'];
16877 } else {
16878 $post = compact('post_date', 'post_content', 'post_title', 'post_status');
16879
16880 $post_id = wp_insert_post($post);
16881 if ( is_wp_error( $post_id ) )
16882 return $post_id;
16883
16884 wp_create_categories( array_map( 'addslashes', $entry->categories ), $post_id );
16885
16886 $author = $this->no_apos( strip_tags( $entry->author ) );
16887
16888 add_post_meta( $post_id, 'blogger_blog', $this->blogs[$importing_blog]['host'], true );
16889 add_post_meta( $post_id, 'blogger_author', $author, true );
16890 add_post_meta( $post_id, 'blogger_permalink', $entry->old_permalink, true );
16891
16892 $this->blogs[$importing_blog]['posts'][$entry->old_permalink] = $post_id;
16893 ++$this->blogs[$importing_blog]['posts_done'];
16894 }
16895 $this->save_vars();
16896 return;
16897 }
16898
16899 function import_comment( $entry ) {
16900 global $importing_blog;
16901
16902 // Drop the #fragment and we have the comment's old post permalink.
16903 foreach ( $entry->links as $link ) {
16904 if ( $link['rel'] == 'alternate' ) {
16905 $parts = parse_url( $link['href'] );
16906 $entry->old_permalink = $parts['fragment'];
16907 $entry->old_post_permalink = $parts['path'];
16908 break;
16909 }
16910 }
16911
16912 $comment_post_ID = (int) $this->blogs[$importing_blog]['posts'][$entry->old_post_permalink];
16913 preg_match('#<name>(.+?)</name>.*(?:\<uri>(.+?)</uri>)?#', $entry->author, $matches);
16914 $comment_author = addslashes( $this->no_apos( strip_tags( (string) $matches[1] ) ) );
16915 $comment_author_url = addslashes( $this->no_apos( strip_tags( (string) $matches[2] ) ) );
16916 $comment_date = $this->convert_date( $entry->updated );
16917 $comment_content = addslashes( $this->no_apos( html_entity_decode( $entry->content ) ) );
16918
16919 // Clean up content
16920 $comment_content = preg_replace('|<(/?[A-Z]+)|e', "'<' . strtolower('$1')", $comment_content);
16921 $comment_content = str_replace('<br>', '<br />', $comment_content);
16922 $comment_content = str_replace('<hr>', '<hr />', $comment_content);
16923
16924 // Checks for duplicates
16925 if (
16926 isset( $this->blogs[$importing_blog]['comments'][$entry->old_permalink] ) ||
16927 comment_exists( $comment_author, $comment_date )
16928 ) {
16929 ++$this->blogs[$importing_blog]['comments_skipped'];
16930 } else {
16931 $comment = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_date', 'comment_content');
16932
16933 $comment_id = wp_insert_comment($comment);
16934
16935 $this->blogs[$importing_blog]['comments'][$entry->old_permalink] = $comment_id;
16936
16937 ++$this->blogs[$importing_blog]['comments_done'];
16938 }
16939 $this->save_vars();
16940 }
16941
16942 function get_js_status($blog = false) {
16943 global $importing_blog;
16944 if ( $blog === false )
16945 $blog = $this->blogs[$importing_blog];
16946 else
16947 $blog = $this->blogs[$blog];
16948 $p1 = isset( $blog['posts_done'] ) ? (int) $blog['posts_done'] : 0;
16949 $p2 = isset( $blog['total_posts'] ) ? (int) $blog['total_posts'] : 0;
16950 $c1 = isset( $blog['comments_done'] ) ? (int) $blog['comments_done'] : 0;
16951 $c2 = isset( $blog['total_comments'] ) ? (int) $blog['total_comments'] : 0;
16952 return "{p1:$p1,p2:$p2,c1:$c1,c2:$c2}";
16953 }
16954
16955 function get_author_form($blog = false) {
16956 global $importing_blog, $wpdb, $current_user;
16957 if ( $blog === false )
16958 $blog = & $this->blogs[$importing_blog];
16959 else
16960 $blog = & $this->blogs[$blog];
16961
16962 if ( !isset( $blog['authors'] ) ) {
16963 $post_ids = array_values($blog['posts']);
16964 $authors = (array) $wpdb->get_col("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN (" . join( ',', $post_ids ) . ")");
16965 $blog['authors'] = array_map(null, $authors, array_fill(0, count($authors), $current_user->ID));
16966 $this->save_vars();
16967 }
16968
16969 $directions = __('All posts were imported with the current user as author. Use this form to move each Blogger user\'s posts to a different WordPress user. You may <a href="users.php">add users</a> and then return to this page and complete the user mapping. This form may be used as many times as you like until you activate the "Restart" function below.');
16970 $heading = __('Author mapping');
16971 $blogtitle = "{$blog['title']} ({$blog['host']})";
16972 $mapthis = __('Blogger username');
16973 $tothis = __('WordPress login');
16974 $submit = js_escape( __('Save Changes') );
16975
16976 foreach ( $blog['authors'] as $i => $author )
16977 $rows .= "<tr><td><label for='authors[$i]'>{$author[0]}</label></td><td><select name='authors[$i]' id='authors[$i]'>" . $this->get_user_options($author[1]) . "</select></td></tr>";
16978
16979 return "<div class='wrap'><h2>$heading</h2><h3>$blogtitle</h3><p>$directions</p><form action='index.php?import=blogger&noheader=true&saveauthors=1' method='post'><input type='hidden' name='blog' value='$importing_blog' /><table cellpadding='5'><thead><td>$mapthis</td><td>$tothis</td></thead>$rows<tr><td></td><td class='submit'><input type='submit' class='button authorsubmit' value='$submit' /></td></tr></table></form></div>";
16980 }
16981
16982 function get_user_options($current) {
16983 global $importer_users;
16984 if ( ! isset( $importer_users ) )
16985 $importer_users = (array) get_users_of_blog();
16986
16987 foreach ( $importer_users as $user ) {
16988 $sel = ( $user->user_id == $current ) ? " selected='selected'" : '';
16989 $options .= "<option value='$user->user_id'$sel>$user->display_name</option>";
16990 }
16991
16992 return $options;
16993 }
16994
16995 function save_authors() {
16996 global $importing_blog, $wpdb;
16997 $authors = (array) $_POST['authors'];
16998
16999 $host = $this->blogs[$importing_blog]['host'];
17000
17001 // Get an array of posts => authors
17002 $post_ids = (array) $wpdb->get_col( $wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'blogger_blog' AND meta_value = %s", $host) );
17003 $post_ids = join( ',', $post_ids );
17004 $results = (array) $wpdb->get_results("SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN ($post_ids)");
17005 foreach ( $results as $row )
17006 $authors_posts[$row->post_id] = $row->meta_value;
17007
17008 foreach ( $authors as $author => $user_id ) {
17009 $user_id = (int) $user_id;
17010
17011 // Skip authors that haven't been changed
17012 if ( $user_id == $this->blogs[$importing_blog]['authors'][$author][1] )
17013 continue;
17014
17015 // Get a list of the selected author's posts
17016 $post_ids = (array) array_keys( $authors_posts, $this->blogs[$importing_blog]['authors'][$author][0] );
17017 $post_ids = join( ',', $post_ids);
17018
17019 $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET post_author = %d WHERE id IN ($post_ids)", $user_id) );
17020 $this->blogs[$importing_blog]['authors'][$author][1] = $user_id;
17021 }
17022 $this->save_vars();
17023
17024 wp_redirect('edit.php');
17025 }
17026
17027 function _get_auth_sock() {
17028 // Connect to https://www.google.com
17029 if ( !$sock = @ fsockopen('ssl://www.google.com', 443, $errno, $errstr) ) {
17030 $this->uh_oh(
17031 __('Could not connect to https://www.google.com'),
17032 __('There was a problem opening a secure connection to Google. This is what went wrong:'),
17033 "$errstr ($errno)"
17034 );
17035 return false;
17036 }
17037 return $sock;
17038 }
17039
17040 function _get_blogger_sock($host = 'www2.blogger.com') {
17041 if ( !$sock = @ fsockopen($host, 80, $errno, $errstr) ) {
17042 $this->uh_oh(
17043 sprintf( __('Could not connect to %s'), $host ),
17044 __('There was a problem opening a connection to Blogger. This is what went wrong:'),
17045 "$errstr ($errno)"
17046 );
17047 return false;
17048 }
17049 return $sock;
17050 }
17051
17052 function _txrx( $sock, $request ) {
17053 fwrite( $sock, $request );
17054 while ( ! feof( $sock ) )
17055 $response .= @ fread ( $sock, 8192 );
17056 fclose( $sock );
17057 return $response;
17058 }
17059
17060 function revoke($token) {
17061 $headers = array(
17062 "GET /accounts/AuthSubRevokeToken HTTP/1.0",
17063 "Authorization: AuthSub token=\"$token\""
17064 );
17065 $request = join( "\r\n", $headers ) . "\r\n\r\n";
17066 $sock = $this->_get_auth_sock( );
17067 if ( ! $sock ) return false;
17068 $this->_txrx( $sock, $request );
17069 }
17070
17071 function restart() {
17072 global $wpdb;
17073 $options = get_option( 'blogger_importer' );
17074
17075 if ( isset( $options['token'] ) )
17076 $this->revoke( $options['token'] );
17077
17078 delete_option('blogger_importer');
17079 $wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = 'blogger_author'");
17080 wp_redirect('?import=blogger');
17081 }
17082
17083 // Returns associative array of code, header, cookies, body. Based on code from php.net.
17084 function parse_response($this_response) {
17085 // Split response into header and body sections
17086 list($response_headers, $response_body) = explode("\r\n\r\n", $this_response, 2);
17087 $response_header_lines = explode("\r\n", $response_headers);
17088
17089 // First line of headers is the HTTP response code
17090 $http_response_line = array_shift($response_header_lines);
17091 if(preg_match('@^HTTP/[0-9]\.[0-9] ([0-9]{3})@',$http_response_line, $matches)) { $response_code = $matches[1]; }
17092
17093 // put the rest of the headers in an array
17094 $response_header_array = array();
17095 foreach($response_header_lines as $header_line) {
17096 list($header,$value) = explode(': ', $header_line, 2);
17097 $response_header_array[$header] .= $value."\n";
17098 }
17099
17100 $cookie_array = array();
17101 $cookies = explode("\n", $response_header_array["Set-Cookie"]);
17102 foreach($cookies as $this_cookie) { array_push($cookie_array, "Cookie: ".$this_cookie); }
17103
17104 return array("code" => $response_code, "header" => $response_header_array, "cookies" => $cookie_array, "body" => $response_body);
17105 }
17106
17107 // Step 9: Congratulate the user
17108 function congrats() {
17109 $blog = (int) $_GET['blog'];
17110 echo '<h1>'.__('Congratulations!').'</h1><p>'.__('Now that you have imported your Blogger blog into WordPress, what are you going to do? Here are some suggestions:').'</p><ul><li>'.__('That was hard work! Take a break.').'</li>';
17111 if ( count($this->import['blogs']) > 1 )
17112 echo '<li>'.__('In case you haven\'t done it already, you can import the posts from your other blogs:'). $this->show_blogs() . '</li>';
17113 if ( $n = count($this->import['blogs'][$blog]['newusers']) )
17114 echo '<li>'.sprintf(__('Go to <a href="%s" target="%s">Authors & Users</a>, where you can modify the new user(s) or delete them. If you want to make all of the imported posts yours, you will be given that option when you delete the new authors.'), 'users.php', '_parent').'</li>';
17115 echo '<li>'.__('For security, click the link below to reset this importer.').'</li>';
17116 echo '</ul>';
17117 }
17118
17119 // Figures out what to do, then does it.
17120 function start() {
17121 if ( isset($_POST['restart']) )
17122 $this->restart();
17123
17124 $options = get_option('blogger_importer');
17125
17126 if ( is_array($options) )
17127 foreach ( $options as $key => $value )
17128 $this->$key = $value;
17129
17130 if ( isset( $_REQUEST['blog'] ) ) {
17131 $blog = is_array($_REQUEST['blog']) ? array_shift( $keys = array_keys( $_REQUEST['blog'] ) ) : $_REQUEST['blog'];
17132 $blog = (int) $blog;
17133 $result = $this->import_blog( $blog );
17134 if ( is_wp_error( $result ) )
17135 echo $result->get_error_message();
17136 } elseif ( isset($_GET['token']) )
17137 $this->auth();
17138 elseif ( isset($this->token) && $this->token_is_valid() )
17139 $this->show_blogs();
17140 else
17141 $this->greet();
17142
17143 $saved = $this->save_vars();
17144
17145 if ( $saved && !isset($_GET['noheader']) ) {
17146 $restart = __('Restart');
17147 $message = __('We have saved some information about your Blogger account in your WordPress database. Clearing this information will allow you to start over. Restarting will not affect any posts you have already imported. If you attempt to re-import a blog, duplicate posts and comments will be skipped.');
17148 $submit = __('Clear account information');
17149 echo "<div class='wrap'><h2>$restart</h2><p>$message</p><form method='post' action='?import=blogger&noheader=true'><p class='submit' style='text-align:left;'><input type='submit' class='button' value='$submit' name='restart' /></p></form></div>";
17150 }
17151 }
17152
17153 function save_vars() {
17154 $vars = get_object_vars($this);
17155 update_option( 'blogger_importer', $vars );
17156
17157 return !empty($vars);
17158 }
17159
17160 function admin_head() {
17161 ?>
17162 <style type="text/css">
17163 td { text-align: center; line-height: 2em;}
17164 thead td { font-weight: bold; }
17165 .bar {
17166 width: 200px;
17167 text-align: left;
17168 line-height: 2em;
17169 padding: 0px;
17170 }
17171 .ind {
17172 position: absolute;
17173 background-color: #83B4D8;
17174 width: 1px;
17175 z-index: 9;
17176 }
17177 .stat {
17178 z-index: 10;
17179 position: relative;
17180 text-align: center;
17181 }
17182 </style>
17183 <?php
17184 }
17185
17186 function Blogger_Import() {
17187 global $importer_started;
17188 $importer_started = time();
17189 if ( isset( $_GET['import'] ) && $_GET['import'] == 'blogger' ) {
17190 wp_enqueue_script('jquery');
17191 add_action('admin_head', array(&$this, 'admin_head'));
17192 }
17193 }
17194 }
17195
17196 $blogger_import = new Blogger_Import();
17197
17198 register_importer('blogger', __('Blogger'), __('Import posts, comments, and users from a Blogger blog.'), array ($blogger_import, 'start'));
17199
17200 class AtomEntry {
17201 var $links = array();
17202 var $categories = array();
17203 }
17204
17205 class AtomParser {
17206
17207 var $ATOM_CONTENT_ELEMENTS = array('content','summary','title','subtitle','rights');
17208 var $ATOM_SIMPLE_ELEMENTS = array('id','updated','published','draft','author');
17209
17210 var $depth = 0;
17211 var $indent = 2;
17212 var $in_content;
17213 var $ns_contexts = array();
17214 var $ns_decls = array();
17215 var $is_xhtml = false;
17216 var $skipped_div = false;
17217
17218 var $entry;
17219
17220 function AtomParser() {
17221
17222 $this->entry = new AtomEntry();
17223 $this->map_attrs_func = create_function('$k,$v', 'return "$k=\"$v\"";');
17224 $this->map_xmlns_func = create_function('$p,$n', '$xd = "xmlns"; if(strlen($n[0])>0) $xd .= ":{$n[0]}"; return "{$xd}=\"{$n[1]}\"";');
17225 }
17226
17227 function parse($xml) {
17228
17229 global $app_logging;
17230 array_unshift($this->ns_contexts, array());
17231
17232 $parser = xml_parser_create_ns();
17233 xml_set_object($parser, $this);
17234 xml_set_element_handler($parser, "start_element", "end_element");
17235 xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0);
17236 xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,0);
17237 xml_set_character_data_handler($parser, "cdata");
17238 xml_set_default_handler($parser, "_default");
17239 xml_set_start_namespace_decl_handler($parser, "start_ns");
17240 xml_set_end_namespace_decl_handler($parser, "end_ns");
17241
17242 $contents = "";
17243
17244 xml_parse($parser, $xml);
17245
17246 xml_parser_free($parser);
17247
17248 return true;
17249 }
17250
17251 function start_element($parser, $name, $attrs) {
17252
17253 $tag = array_pop(split(":", $name));
17254
17255 array_unshift($this->ns_contexts, $this->ns_decls);
17256
17257 $this->depth++;
17258
17259 if(!empty($this->in_content)) {
17260 $attrs_prefix = array();
17261
17262 // resolve prefixes for attributes
17263 foreach($attrs as $key => $value) {
17264 $attrs_prefix[$this->ns_to_prefix($key)] = $this->xml_escape($value);
17265 }
17266 $attrs_str = join(' ', array_map($this->map_attrs_func, array_keys($attrs_prefix), array_values($attrs_prefix)));
17267 if(strlen($attrs_str) > 0) {
17268 $attrs_str = " " . $attrs_str;
17269 }
17270
17271 $xmlns_str = join(' ', array_map($this->map_xmlns_func, array_keys($this->ns_contexts[0]), array_values($this->ns_contexts[0])));
17272 if(strlen($xmlns_str) > 0) {
17273 $xmlns_str = " " . $xmlns_str;
17274 }
17275
17276 // handle self-closing tags (case: a new child found right-away, no text node)
17277 if(count($this->in_content) == 2) {
17278 array_push($this->in_content, ">");
17279 }
17280
17281 array_push($this->in_content, "<". $this->ns_to_prefix($name) ."{$xmlns_str}{$attrs_str}");
17282 } else if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS) || in_array($tag, $this->ATOM_SIMPLE_ELEMENTS)) {
17283 $this->in_content = array();
17284 $this->is_xhtml = $attrs['type'] == 'xhtml';
17285 array_push($this->in_content, array($tag,$this->depth));
17286 } else if($tag == 'link') {
17287 array_push($this->entry->links, $attrs);
17288 } else if($tag == 'category') {
17289 array_push($this->entry->categories, $attrs['term']);
17290 }
17291
17292 $this->ns_decls = array();
17293 }
17294
17295 function end_element($parser, $name) {
17296
17297 $tag = array_pop(split(":", $name));
17298
17299 if(!empty($this->in_content)) {
17300 if($this->in_content[0][0] == $tag &&
17301 $this->in_content[0][1] == $this->depth) {
17302 array_shift($this->in_content);
17303 if($this->is_xhtml) {
17304 $this->in_content = array_slice($this->in_content, 2, count($this->in_content)-3);
17305 }
17306 $this->entry->$tag = join('',$this->in_content);
17307 $this->in_content = array();
17308 } else {
17309 $endtag = $this->ns_to_prefix($name);
17310 if (strpos($this->in_content[count($this->in_content)-1], '<' . $endtag) !== false) {
17311 array_push($this->in_content, "/>");
17312 } else {
17313 array_push($this->in_content, "</$endtag>");
17314 }
17315 }
17316 }
17317
17318 array_shift($this->ns_contexts);
17319
17320 #print str_repeat(" ", $this->depth * $this->indent) . "end_element('$name')" ."\n";
17321
17322 $this->depth--;
17323 }
17324
17325 function start_ns($parser, $prefix, $uri) {
17326 #print str_repeat(" ", $this->depth * $this->indent) . "starting: " . $prefix . ":" . $uri . "\n";
17327 array_push($this->ns_decls, array($prefix,$uri));
17328 }
17329
17330 function end_ns($parser, $prefix) {
17331 #print str_repeat(" ", $this->depth * $this->indent) . "ending: #" . $prefix . "#\n";
17332 }
17333
17334 function cdata($parser, $data) {
17335 #print str_repeat(" ", $this->depth * $this->indent) . "data: #" . $data . "#\n";
17336 if(!empty($this->in_content)) {
17337 // handle self-closing tags (case: text node found, need to close element started)
17338 if (strpos($this->in_content[count($this->in_content)-1], '<') !== false) {
17339 array_push($this->in_content, ">");
17340 }
17341 array_push($this->in_content, $this->xml_escape($data));
17342 }
17343 }
17344
17345 function _default($parser, $data) {
17346 # when does this gets called?
17347 }
17348
17349
17350 function ns_to_prefix($qname) {
17351 $components = split(":", $qname);
17352 $name = array_pop($components);
17353
17354 if(!empty($components)) {
17355 $ns = join(":",$components);
17356 foreach($this->ns_contexts as $context) {
17357 foreach($context as $mapping) {
17358 if($mapping[1] == $ns && strlen($mapping[0]) > 0) {
17359 return "$mapping[0]:$name";
17360 }
17361 }
17362 }
17363 }
17364 return $name;
17365 }
17366
17367 function xml_escape($string)
17368 {
17369 return str_replace(array('&','"',"'",'<','>'),
17370 array('&','"',''','<','>'),
17371 $string );
17372 }
17373 }
17374
17375 ?>