| [ Index ] |
PHP Cross Reference of Wordpress 2.9.1 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Blogger Importer 4 * 5 * @package WordPress 6 * @subpackage Importer 7 */ 8 9 /** 10 * How many records per GData query 11 * 12 * @package WordPress 13 * @subpackage Blogger_Import 14 * @var int 15 * @since unknown 16 */ 17 define( 'MAX_RESULTS', 50 ); 18 19 /** 20 * How many seconds to let the script run 21 * 22 * @package WordPress 23 * @subpackage Blogger_Import 24 * @var int 25 * @since unknown 26 */ 27 define( 'MAX_EXECUTION_TIME', 20 ); 28 29 /** 30 * How many seconds between status bar updates 31 * 32 * @package WordPress 33 * @subpackage Blogger_Import 34 * @var int 35 * @since unknown 36 */ 37 define( 'STATUS_INTERVAL', 3 ); 38 39 /** 40 * Blogger Importer class 41 * 42 * @since unknown 43 */ 44 class Blogger_Import { 45 46 // Shows the welcome screen and the magic auth link. 47 function greet() { 48 $next_url = get_option('siteurl') . '/wp-admin/index.php?import=blogger&noheader=true'; 49 $auth_url = "https://www.google.com/accounts/AuthSubRequest"; 50 $title = __('Import Blogger'); 51 $welcome = __('Howdy! This importer allows you to import posts and comments from your Blogger account into your WordPress blog.'); 52 $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).'); 53 $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.'); 54 $auth = esc_attr__('Authorize'); 55 56 echo " 57 <div class='wrap'> 58 ".screen_icon()." 59 <h2>$title</h2> 60 <p>$welcome</p><p>$prereqs</p><p>$stepone</p> 61 <form action='$auth_url' method='get'> 62 <p class='submit' style='text-align:left;'> 63 <input type='submit' class='button' value='$auth' /> 64 <input type='hidden' name='scope' value='http://www.blogger.com/feeds/' /> 65 <input type='hidden' name='session' value='1' /> 66 <input type='hidden' name='secure' value='0' /> 67 <input type='hidden' name='next' value='$next_url' /> 68 </p> 69 </form> 70 </div>\n"; 71 } 72 73 function uh_oh($title, $message, $info) { 74 echo "<div class='wrap'>"; 75 screen_icon(); 76 echo "<h2>$title</h2><p>$message</p><pre>$info</pre></div>"; 77 } 78 79 function auth() { 80 // We have a single-use token that must be upgraded to a session token. 81 $token = preg_replace( '/[^-_0-9a-zA-Z]/', '', $_GET['token'] ); 82 $headers = array( 83 "GET /accounts/AuthSubSessionToken HTTP/1.0", 84 "Authorization: AuthSub token=\"$token\"" 85 ); 86 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 87 $sock = $this->_get_auth_sock( ); 88 if ( ! $sock ) return false; 89 $response = $this->_txrx( $sock, $request ); 90 preg_match( '/token=([-_0-9a-z]+)/i', $response, $matches ); 91 if ( empty( $matches[1] ) ) { 92 $this->uh_oh( 93 __( 'Authorization failed' ), 94 __( 'Something went wrong. If the problem persists, send this info to support:' ), 95 htmlspecialchars($response) 96 ); 97 return false; 98 } 99 $this->token = $matches[1]; 100 101 wp_redirect( remove_query_arg( array( 'token', 'noheader' ) ) ); 102 } 103 104 function get_token_info() { 105 $headers = array( 106 "GET /accounts/AuthSubTokenInfo HTTP/1.0", 107 "Authorization: AuthSub token=\"$this->token\"" 108 ); 109 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 110 $sock = $this->_get_auth_sock( ); 111 if ( ! $sock ) return; 112 $response = $this->_txrx( $sock, $request ); 113 return $this->parse_response($response); 114 } 115 116 function token_is_valid() { 117 $info = $this->get_token_info(); 118 119 if ( $info['code'] == 200 ) 120 return true; 121 122 return false; 123 } 124 125 function show_blogs($iter = 0) { 126 if ( empty($this->blogs) ) { 127 $headers = array( 128 "GET /feeds/default/blogs HTTP/1.0", 129 "Host: www.blogger.com", 130 "Authorization: AuthSub token=\"$this->token\"" 131 ); 132 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 133 $sock = $this->_get_blogger_sock( ); 134 if ( ! $sock ) return; 135 $response = $this->_txrx( $sock, $request ); 136 137 // Quick and dirty XML mining. 138 list( $headers, $xml ) = explode( "\r\n\r\n", $response ); 139 $p = xml_parser_create(); 140 xml_parse_into_struct($p, $xml, $vals, $index); 141 xml_parser_free($p); 142 143 $this->title = $vals[$index['TITLE'][0]]['value']; 144 145 // Give it a few retries... this step often flakes out the first time. 146 if ( empty( $index['ENTRY'] ) ) { 147 if ( $iter < 3 ) { 148 return $this->show_blogs($iter + 1); 149 } else { 150 $this->uh_oh( 151 __('Trouble signing in'), 152 __('We were not able to gain access to your account. Try starting over.'), 153 '' 154 ); 155 return false; 156 } 157 } 158 159 foreach ( $index['ENTRY'] as $i ) { 160 $blog = array(); 161 while ( ( $tag = $vals[$i] ) && ! ( $tag['tag'] == 'ENTRY' && $tag['type'] == 'close' ) ) { 162 if ( $tag['tag'] == 'TITLE' ) { 163 $blog['title'] = $tag['value']; 164 } elseif ( $tag['tag'] == 'SUMMARY' ) { 165 $blog['summary'] == $tag['value']; 166 } elseif ( $tag['tag'] == 'LINK' ) { 167 if ( $tag['attributes']['REL'] == 'alternate' && $tag['attributes']['TYPE'] == 'text/html' ) { 168 $parts = parse_url( $tag['attributes']['HREF'] ); 169 $blog['host'] = $parts['host']; 170 } elseif ( $tag['attributes']['REL'] == 'edit' ) 171 $blog['gateway'] = $tag['attributes']['HREF']; 172 } 173 ++$i; 174 } 175 if ( ! empty ( $blog ) ) { 176 $blog['total_posts'] = $this->get_total_results('posts', $blog['host']); 177 $blog['total_comments'] = $this->get_total_results('comments', $blog['host']); 178 $blog['mode'] = 'init'; 179 $this->blogs[] = $blog; 180 } 181 } 182 183 if ( empty( $this->blogs ) ) { 184 $this->uh_oh( 185 __('No blogs found'), 186 __('We were able to log in but there were no blogs. Try a different account next time.'), 187 '' 188 ); 189 return false; 190 } 191 } 192 //echo '<pre>'.print_r($this,1).'</pre>'; 193 $start = esc_js( __('Import') ); 194 $continue = esc_js( __('Continue') ); 195 $stop = esc_js( __('Importing...') ); 196 $authors = esc_js( __('Set Authors') ); 197 $loadauth = esc_js( __('Preparing author mapping form...') ); 198 $authhead = esc_js( __('Final Step: Author Mapping') ); 199 $nothing = esc_js( __('Nothing was imported. Had you already imported this blog?') ); 200 $stopping = ''; //Missing String used below. 201 $title = __('Blogger Blogs'); 202 $name = __('Blog Name'); 203 $url = __('Blog URL'); 204 $action = __('The Magic Button'); 205 $posts = __('Posts'); 206 $comments = __('Comments'); 207 $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.'); 208 209 $interval = STATUS_INTERVAL * 1000; 210 211 foreach ( $this->blogs as $i => $blog ) { 212 if ( $blog['mode'] == 'init' ) 213 $value = $start; 214 elseif ( $blog['mode'] == 'posts' || $blog['mode'] == 'comments' ) 215 $value = $continue; 216 else 217 $value = $authors; 218 $value = esc_attr($value); 219 $blogtitle = esc_js( $blog['title'] ); 220 $pdone = isset($blog['posts_done']) ? (int) $blog['posts_done'] : 0; 221 $cdone = isset($blog['comments_done']) ? (int) $blog['comments_done'] : 0; 222 $init .= "blogs[$i]=new blog($i,'$blogtitle','{$blog['mode']}'," . $this->get_js_status($i) . ');'; 223 $pstat = "<div class='ind' id='pind$i'> </div><div id='pstat$i' class='stat'>$pdone/{$blog['total_posts']}</div>"; 224 $cstat = "<div class='ind' id='cind$i'> </div><div id='cstat$i' class='stat'>$cdone/{$blog['total_comments']}</div>"; 225 $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"; 226 } 227 228 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>"; 229 echo " 230 <script type='text/javascript'> 231 /* <![CDATA[ */ 232 var strings = {cont:'$continue',stop:'$stop',stopping:'$stopping',authors:'$authors',nothing:'$nothing'}; 233 var blogs = {}; 234 function blog(i, title, mode, status){ 235 this.blog = i; 236 this.mode = mode; 237 this.title = title; 238 this.status = status; 239 this.button = document.getElementById('submit'+this.blog); 240 }; 241 blog.prototype = { 242 start: function() { 243 this.cont = true; 244 this.kick(); 245 this.check(); 246 }, 247 kick: function() { 248 ++this.kicks; 249 var i = this.blog; 250 jQuery.post('admin.php?import=blogger&noheader=true',{blog:this.blog},function(text,result){blogs[i].kickd(text,result)}); 251 }, 252 check: function() { 253 ++this.checks; 254 var i = this.blog; 255 jQuery.post('admin.php?import=blogger&noheader=true&status=true',{blog:this.blog},function(text,result){blogs[i].checkd(text,result)}); 256 }, 257 kickd: function(text, result) { 258 if ( result == 'error' ) { 259 // TODO: exception handling 260 if ( this.cont ) 261 setTimeout('blogs['+this.blog+'].kick()', 1000); 262 } else { 263 if ( text == 'done' ) { 264 this.stop(); 265 this.done(); 266 } else if ( text == 'nothing' ) { 267 this.stop(); 268 this.nothing(); 269 } else if ( text == 'continue' ) { 270 this.kick(); 271 } else if ( this.mode = 'stopped' ) 272 jQuery(this.button).attr('value', strings.cont); 273 } 274 --this.kicks; 275 }, 276 checkd: function(text, result) { 277 if ( result == 'error' ) { 278 // TODO: exception handling 279 } else { 280 eval('this.status='+text); 281 jQuery('#pstat'+this.blog).empty().append(this.status.p1+'/'+this.status.p2); 282 jQuery('#cstat'+this.blog).empty().append(this.status.c1+'/'+this.status.c2); 283 this.update(); 284 if ( this.cont || this.kicks > 0 ) 285 setTimeout('blogs['+this.blog+'].check()', $interval); 286 } 287 --this.checks; 288 }, 289 update: function() { 290 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'); 291 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'); 292 }, 293 stop: function() { 294 this.cont = false; 295 }, 296 done: function() { 297 this.mode = 'authors'; 298 jQuery(this.button).attr('value', strings.authors); 299 }, 300 nothing: function() { 301 this.mode = 'nothing'; 302 jQuery(this.button).remove(); 303 alert(strings.nothing); 304 }, 305 getauthors: function() { 306 if ( jQuery('div.wrap').length > 1 ) 307 jQuery('div.wrap').gt(0).remove(); 308 jQuery('div.wrap').empty().append('<h2>$authhead</h2><h3>' + this.title + '</h3>'); 309 jQuery('div.wrap').append('<p id=\"auth\">$loadauth</p>'); 310 jQuery('p#auth').load('index.php?import=blogger&noheader=true&authors=1',{blog:this.blog}); 311 }, 312 init: function() { 313 this.update(); 314 var i = this.blog; 315 jQuery(this.button).bind('click', function(){return blogs[i].click();}); 316 this.kicks = 0; 317 this.checks = 0; 318 }, 319 click: function() { 320 if ( this.mode == 'init' || this.mode == 'stopped' || this.mode == 'posts' || this.mode == 'comments' ) { 321 this.mode = 'started'; 322 this.start(); 323 jQuery(this.button).attr('value', strings.stop); 324 } else if ( this.mode == 'started' ) { 325 return false; // let it run... 326 this.mode = 'stopped'; 327 this.stop(); 328 if ( this.checks > 0 || this.kicks > 0 ) { 329 this.mode = 'stopping'; 330 jQuery(this.button).attr('value', strings.stopping); 331 } else { 332 jQuery(this.button).attr('value', strings.cont); 333 } 334 } else if ( this.mode == 'authors' ) { 335 document.location = 'index.php?import=blogger&authors=1&blog='+this.blog; 336 //this.mode = 'authors2'; 337 //this.getauthors(); 338 } 339 return false; 340 } 341 }; 342 $init 343 jQuery.each(blogs, function(i, me){me.init();}); 344 /* ]]> */ 345 </script>\n"; 346 } 347 348 // Handy function for stopping the script after a number of seconds. 349 function have_time() { 350 global $importer_started; 351 if ( time() - $importer_started > MAX_EXECUTION_TIME ) 352 die('continue'); 353 return true; 354 } 355 356 function get_total_results($type, $host) { 357 $headers = array( 358 "GET /feeds/$type/default?max-results=1&start-index=2 HTTP/1.0", 359 "Host: $host", 360 "Authorization: AuthSub token=\"$this->token\"" 361 ); 362 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 363 $sock = $this->_get_blogger_sock( $host ); 364 if ( ! $sock ) return; 365 $response = $this->_txrx( $sock, $request ); 366 $response = $this->parse_response( $response ); 367 $parser = xml_parser_create(); 368 xml_parse_into_struct($parser, $response['body'], $struct, $index); 369 xml_parser_free($parser); 370 $total_results = $struct[$index['OPENSEARCH:TOTALRESULTS'][0]]['value']; 371 return (int) $total_results; 372 } 373 374 function import_blog($blogID) { 375 global $importing_blog; 376 $importing_blog = $blogID; 377 378 if ( isset($_GET['authors']) ) 379 return print($this->get_author_form()); 380 381 header('Content-Type: text/plain'); 382 383 if ( isset($_GET['status']) ) 384 die($this->get_js_status()); 385 386 if ( isset($_GET['saveauthors']) ) 387 die($this->save_authors()); 388 389 $blog = $this->blogs[$blogID]; 390 $total_results = $this->get_total_results('posts', $blog['host']); 391 $this->blogs[$importing_blog]['total_posts'] = $total_results; 392 393 $start_index = $total_results - MAX_RESULTS + 1; 394 395 if ( isset( $this->blogs[$importing_blog]['posts_start_index'] ) ) 396 $start_index = (int) $this->blogs[$importing_blog]['posts_start_index']; 397 elseif ( $total_results > MAX_RESULTS ) 398 $start_index = $total_results - MAX_RESULTS + 1; 399 else 400 $start_index = 1; 401 402 // This will be positive until we have finished importing posts 403 if ( $start_index > 0 ) { 404 // Grab all the posts 405 $this->blogs[$importing_blog]['mode'] = 'posts'; 406 $query = "start-index=$start_index&max-results=" . MAX_RESULTS; 407 do { 408 $index = $struct = $entries = array(); 409 $headers = array( 410 "GET /feeds/posts/default?$query HTTP/1.0", 411 "Host: {$blog['host']}", 412 "Authorization: AuthSub token=\"$this->token\"" 413 ); 414 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 415 $sock = $this->_get_blogger_sock( $blog['host'] ); 416 if ( ! $sock ) return; // TODO: Error handling 417 $response = $this->_txrx( $sock, $request ); 418 419 $response = $this->parse_response( $response ); 420 421 // Extract the entries and send for insertion 422 preg_match_all( '/<entry[^>]*>.*?<\/entry>/s', $response['body'], $matches ); 423 if ( count( $matches[0] ) ) { 424 $entries = array_reverse($matches[0]); 425 foreach ( $entries as $entry ) { 426 $entry = "<feed>$entry</feed>"; 427 $AtomParser = new AtomParser(); 428 $AtomParser->parse( $entry ); 429 $result = $this->import_post($AtomParser->entry); 430 if ( is_wp_error( $result ) ) 431 return $result; 432 unset($AtomParser); 433 } 434 } else break; 435 436 // Get the 'previous' query string which we'll use on the next iteration 437 $query = ''; 438 $links = preg_match_all('/<link([^>]*)>/', $response['body'], $matches); 439 if ( count( $matches[1] ) ) 440 foreach ( $matches[1] as $match ) 441 if ( preg_match('/rel=.previous./', $match) ) 442 $query = @html_entity_decode( preg_replace('/^.*href=[\'"].*\?(.+)[\'"].*$/', '$1', $match), ENT_COMPAT, get_option('blog_charset') ); 443 444 if ( $query ) { 445 parse_str($query, $q); 446 $this->blogs[$importing_blog]['posts_start_index'] = (int) $q['start-index']; 447 } else 448 $this->blogs[$importing_blog]['posts_start_index'] = 0; 449 $this->save_vars(); 450 } while ( !empty( $query ) && $this->have_time() ); 451 } 452 453 $total_results = $this->get_total_results( 'comments', $blog['host'] ); 454 $this->blogs[$importing_blog]['total_comments'] = $total_results; 455 456 if ( isset( $this->blogs[$importing_blog]['comments_start_index'] ) ) 457 $start_index = (int) $this->blogs[$importing_blog]['comments_start_index']; 458 elseif ( $total_results > MAX_RESULTS ) 459 $start_index = $total_results - MAX_RESULTS + 1; 460 else 461 $start_index = 1; 462 463 if ( $start_index > 0 ) { 464 // Grab all the comments 465 $this->blogs[$importing_blog]['mode'] = 'comments'; 466 $query = "start-index=$start_index&max-results=" . MAX_RESULTS; 467 do { 468 $index = $struct = $entries = array(); 469 $headers = array( 470 "GET /feeds/comments/default?$query HTTP/1.0", 471 "Host: {$blog['host']}", 472 "Authorization: AuthSub token=\"$this->token\"" 473 ); 474 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 475 $sock = $this->_get_blogger_sock( $blog['host'] ); 476 if ( ! $sock ) return; // TODO: Error handling 477 $response = $this->_txrx( $sock, $request ); 478 479 $response = $this->parse_response( $response ); 480 481 // Extract the comments and send for insertion 482 preg_match_all( '/<entry[^>]*>.*?<\/entry>/s', $response['body'], $matches ); 483 if ( count( $matches[0] ) ) { 484 $entries = array_reverse( $matches[0] ); 485 foreach ( $entries as $entry ) { 486 $entry = "<feed>$entry</feed>"; 487 $AtomParser = new AtomParser(); 488 $AtomParser->parse( $entry ); 489 $this->import_comment($AtomParser->entry); 490 unset($AtomParser); 491 } 492 } 493 494 // Get the 'previous' query string which we'll use on the next iteration 495 $query = ''; 496 $links = preg_match_all('/<link([^>]*)>/', $response['body'], $matches); 497 if ( count( $matches[1] ) ) 498 foreach ( $matches[1] as $match ) 499 if ( preg_match('/rel=.previous./', $match) ) 500 $query = @html_entity_decode( preg_replace('/^.*href=[\'"].*\?(.+)[\'"].*$/', '$1', $match), ENT_COMPAT, get_option('blog_charset') ); 501 502 parse_str($query, $q); 503 504 $this->blogs[$importing_blog]['comments_start_index'] = (int) $q['start-index']; 505 $this->save_vars(); 506 } while ( !empty( $query ) && $this->have_time() ); 507 } 508 $this->blogs[$importing_blog]['mode'] = 'authors'; 509 $this->save_vars(); 510 if ( !$this->blogs[$importing_blog]['posts_done'] && !$this->blogs[$importing_blog]['comments_done'] ) 511 die('nothing'); 512 do_action('import_done', 'blogger'); 513 die('done'); 514 } 515 516 function convert_date( $date ) { 517 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); 518 $offset = iso8601_timezone_to_offset( $date_bits[7] ); 519 $timestamp = gmmktime($date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1]); 520 $timestamp -= $offset; // Convert from Blogger local time to GMT 521 $timestamp += get_option('gmt_offset') * 3600; // Convert from GMT to WP local time 522 return gmdate('Y-m-d H:i:s', $timestamp); 523 } 524 525 function no_apos( $string ) { 526 return str_replace( ''', "'", $string); 527 } 528 529 function min_whitespace( $string ) { 530 return preg_replace( '|\s+|', ' ', $string ); 531 } 532 533 function _normalize_tag( $matches ) { 534 return '<' . strtolower( $matches[1] ); 535 } 536 537 function import_post( $entry ) { 538 global $importing_blog; 539 540 // The old permalink is all Blogger gives us to link comments to their posts. 541 if ( isset( $entry->draft ) ) 542 $rel = 'self'; 543 else 544 $rel = 'alternate'; 545 foreach ( $entry->links as $link ) { 546 if ( $link['rel'] == $rel ) { 547 $parts = parse_url( $link['href'] ); 548 $entry->old_permalink = $parts['path']; 549 break; 550 } 551 } 552 553 $post_date = $this->convert_date( $entry->published ); 554 $post_content = trim( addslashes( $this->no_apos( @html_entity_decode( $entry->content, ENT_COMPAT, get_option('blog_charset') ) ) ) ); 555 $post_title = trim( addslashes( $this->no_apos( $this->min_whitespace( $entry->title ) ) ) ); 556 $post_status = isset( $entry->draft ) ? 'draft' : 'publish'; 557 558 // Clean up content 559 $post_content = preg_replace_callback('|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $post_content); 560 $post_content = str_replace('<br>', '<br />', $post_content); 561 $post_content = str_replace('<hr>', '<hr />', $post_content); 562 563 // Checks for duplicates 564 if ( isset( $this->blogs[$importing_blog]['posts'][$entry->old_permalink] ) ) { 565 ++$this->blogs[$importing_blog]['posts_skipped']; 566 } elseif ( $post_id = post_exists( $post_title, $post_content, $post_date ) ) { 567 $this->blogs[$importing_blog]['posts'][$entry->old_permalink] = $post_id; 568 ++$this->blogs[$importing_blog]['posts_skipped']; 569 } else { 570 $post = compact('post_date', 'post_content', 'post_title', 'post_status'); 571 572 $post_id = wp_insert_post($post); 573 if ( is_wp_error( $post_id ) ) 574 return $post_id; 575 576 wp_create_categories( array_map( 'addslashes', $entry->categories ), $post_id ); 577 578 $author = $this->no_apos( strip_tags( $entry->author ) ); 579 580 add_post_meta( $post_id, 'blogger_blog', $this->blogs[$importing_blog]['host'], true ); 581 add_post_meta( $post_id, 'blogger_author', $author, true ); 582 add_post_meta( $post_id, 'blogger_permalink', $entry->old_permalink, true ); 583 584 $this->blogs[$importing_blog]['posts'][$entry->old_permalink] = $post_id; 585 ++$this->blogs[$importing_blog]['posts_done']; 586 } 587 $this->save_vars(); 588 return; 589 } 590 591 function import_comment( $entry ) { 592 global $importing_blog; 593 594 // Drop the #fragment and we have the comment's old post permalink. 595 foreach ( $entry->links as $link ) { 596 if ( $link['rel'] == 'alternate' ) { 597 $parts = parse_url( $link['href'] ); 598 $entry->old_permalink = $parts['fragment']; 599 $entry->old_post_permalink = $parts['path']; 600 break; 601 } 602 } 603 604 $comment_post_ID = (int) $this->blogs[$importing_blog]['posts'][$entry->old_post_permalink]; 605 preg_match('#<name>(.+?)</name>.*(?:\<uri>(.+?)</uri>)?#', $entry->author, $matches); 606 $comment_author = addslashes( $this->no_apos( strip_tags( (string) $matches[1] ) ) ); 607 $comment_author_url = addslashes( $this->no_apos( strip_tags( (string) $matches[2] ) ) ); 608 $comment_date = $this->convert_date( $entry->updated ); 609 $comment_content = addslashes( $this->no_apos( @html_entity_decode( $entry->content, ENT_COMPAT, get_option('blog_charset') ) ) ); 610 611 // Clean up content 612 $comment_content = preg_replace_callback('|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $comment_content); 613 $comment_content = str_replace('<br>', '<br />', $comment_content); 614 $comment_content = str_replace('<hr>', '<hr />', $comment_content); 615 616 // Checks for duplicates 617 if ( 618 isset( $this->blogs[$importing_blog]['comments'][$entry->old_permalink] ) || 619 comment_exists( $comment_author, $comment_date ) 620 ) { 621 ++$this->blogs[$importing_blog]['comments_skipped']; 622 } else { 623 $comment = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_date', 'comment_content'); 624 625 $comment = wp_filter_comment($comment); 626 $comment_id = wp_insert_comment($comment); 627 628 $this->blogs[$importing_blog]['comments'][$entry->old_permalink] = $comment_id; 629 630 ++$this->blogs[$importing_blog]['comments_done']; 631 } 632 $this->save_vars(); 633 } 634 635 function get_js_status($blog = false) { 636 global $importing_blog; 637 if ( $blog === false ) 638 $blog = $this->blogs[$importing_blog]; 639 else 640 $blog = $this->blogs[$blog]; 641 $p1 = isset( $blog['posts_done'] ) ? (int) $blog['posts_done'] : 0; 642 $p2 = isset( $blog['total_posts'] ) ? (int) $blog['total_posts'] : 0; 643 $c1 = isset( $blog['comments_done'] ) ? (int) $blog['comments_done'] : 0; 644 $c2 = isset( $blog['total_comments'] ) ? (int) $blog['total_comments'] : 0; 645 return "{p1:$p1,p2:$p2,c1:$c1,c2:$c2}"; 646 } 647 648 function get_author_form($blog = false) { 649 global $importing_blog, $wpdb, $current_user; 650 if ( $blog === false ) 651 $blog = & $this->blogs[$importing_blog]; 652 else 653 $blog = & $this->blogs[$blog]; 654 655 if ( !isset( $blog['authors'] ) ) { 656 $post_ids = array_values($blog['posts']); 657 $authors = (array) $wpdb->get_col("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN (" . join( ',', $post_ids ) . ")"); 658 $blog['authors'] = array_map(null, $authors, array_fill(0, count($authors), $current_user->ID)); 659 $this->save_vars(); 660 } 661 662 $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.'); 663 $heading = __('Author mapping'); 664 $blogtitle = "{$blog['title']} ({$blog['host']})"; 665 $mapthis = __('Blogger username'); 666 $tothis = __('WordPress login'); 667 $submit = esc_js( __('Save Changes') ); 668 669 foreach ( $blog['authors'] as $i => $author ) 670 $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>"; 671 672 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='" . esc_attr($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>"; 673 } 674 675 function get_user_options($current) { 676 global $importer_users; 677 if ( ! isset( $importer_users ) ) 678 $importer_users = (array) get_users_of_blog(); 679 680 foreach ( $importer_users as $user ) { 681 $sel = ( $user->user_id == $current ) ? " selected='selected'" : ''; 682 $options .= "<option value='$user->user_id'$sel>$user->display_name</option>"; 683 } 684 685 return $options; 686 } 687 688 function save_authors() { 689 global $importing_blog, $wpdb; 690 $authors = (array) $_POST['authors']; 691 692 $host = $this->blogs[$importing_blog]['host']; 693 694 // Get an array of posts => authors 695 $post_ids = (array) $wpdb->get_col( $wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'blogger_blog' AND meta_value = %s", $host) ); 696 $post_ids = join( ',', $post_ids ); 697 $results = (array) $wpdb->get_results("SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN ($post_ids)"); 698 foreach ( $results as $row ) 699 $authors_posts[$row->post_id] = $row->meta_value; 700 701 foreach ( $authors as $author => $user_id ) { 702 $user_id = (int) $user_id; 703 704 // Skip authors that haven't been changed 705 if ( $user_id == $this->blogs[$importing_blog]['authors'][$author][1] ) 706 continue; 707 708 // Get a list of the selected author's posts 709 $post_ids = (array) array_keys( $authors_posts, $this->blogs[$importing_blog]['authors'][$author][0] ); 710 $post_ids = join( ',', $post_ids); 711 712 $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET post_author = %d WHERE id IN ($post_ids)", $user_id) ); 713 $this->blogs[$importing_blog]['authors'][$author][1] = $user_id; 714 } 715 $this->save_vars(); 716 717 wp_redirect('edit.php'); 718 } 719 720 function _get_auth_sock() { 721 // Connect to https://www.google.com 722 if ( !$sock = @ fsockopen('ssl://www.google.com', 443, $errno, $errstr) ) { 723 $this->uh_oh( 724 __('Could not connect to https://www.google.com'), 725 __('There was a problem opening a secure connection to Google. This is what went wrong:'), 726 "$errstr ($errno)" 727 ); 728 return false; 729 } 730 return $sock; 731 } 732 733 function _get_blogger_sock($host = 'www2.blogger.com') { 734 if ( !$sock = @ fsockopen($host, 80, $errno, $errstr) ) { 735 $this->uh_oh( 736 sprintf( __('Could not connect to %s'), $host ), 737 __('There was a problem opening a connection to Blogger. This is what went wrong:'), 738 "$errstr ($errno)" 739 ); 740 return false; 741 } 742 return $sock; 743 } 744 745 function _txrx( $sock, $request ) { 746 fwrite( $sock, $request ); 747 while ( ! feof( $sock ) ) 748 $response .= @ fread ( $sock, 8192 ); 749 fclose( $sock ); 750 return $response; 751 } 752 753 function revoke($token) { 754 $headers = array( 755 "GET /accounts/AuthSubRevokeToken HTTP/1.0", 756 "Authorization: AuthSub token=\"$token\"" 757 ); 758 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 759 $sock = $this->_get_auth_sock( ); 760 if ( ! $sock ) return false; 761 $this->_txrx( $sock, $request ); 762 } 763 764 function restart() { 765 global $wpdb; 766 $options = get_option( 'blogger_importer' ); 767 768 if ( isset( $options['token'] ) ) 769 $this->revoke( $options['token'] ); 770 771 delete_option('blogger_importer'); 772 $wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = 'blogger_author'"); 773 wp_redirect('?import=blogger'); 774 } 775 776 // Returns associative array of code, header, cookies, body. Based on code from php.net. 777 function parse_response($this_response) { 778 // Split response into header and body sections 779 list($response_headers, $response_body) = explode("\r\n\r\n", $this_response, 2); 780 $response_header_lines = explode("\r\n", $response_headers); 781 782 // First line of headers is the HTTP response code 783 $http_response_line = array_shift($response_header_lines); 784 if(preg_match('@^HTTP/[0-9]\.[0-9] ([0-9]{3})@',$http_response_line, $matches)) { $response_code = $matches[1]; } 785 786 // put the rest of the headers in an array 787 $response_header_array = array(); 788 foreach($response_header_lines as $header_line) { 789 list($header,$value) = explode(': ', $header_line, 2); 790 $response_header_array[$header] .= $value."\n"; 791 } 792 793 $cookie_array = array(); 794 $cookies = explode("\n", $response_header_array["Set-Cookie"]); 795 foreach($cookies as $this_cookie) { array_push($cookie_array, "Cookie: ".$this_cookie); } 796 797 return array("code" => $response_code, "header" => $response_header_array, "cookies" => $cookie_array, "body" => $response_body); 798 } 799 800 // Step 9: Congratulate the user 801 function congrats() { 802 $blog = (int) $_GET['blog']; 803 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>'; 804 if ( count($this->import['blogs']) > 1 ) 805 echo '<li>'.__('In case you haven’t done it already, you can import the posts from your other blogs:'). $this->show_blogs() . '</li>'; 806 if ( $n = count($this->import['blogs'][$blog]['newusers']) ) 807 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>'; 808 echo '<li>'.__('For security, click the link below to reset this importer.').'</li>'; 809 echo '</ul>'; 810 } 811 812 // Figures out what to do, then does it. 813 function start() { 814 if ( isset($_POST['restart']) ) 815 $this->restart(); 816 817 $options = get_option('blogger_importer'); 818 819 if ( is_array($options) ) 820 foreach ( $options as $key => $value ) 821 $this->$key = $value; 822 823 if ( isset( $_REQUEST['blog'] ) ) { 824 $blog = is_array($_REQUEST['blog']) ? array_shift( $keys = array_keys( $_REQUEST['blog'] ) ) : $_REQUEST['blog']; 825 $blog = (int) $blog; 826 $result = $this->import_blog( $blog ); 827 if ( is_wp_error( $result ) ) 828 echo $result->get_error_message(); 829 } elseif ( isset($_GET['token']) ) 830 $this->auth(); 831 elseif ( isset($this->token) && $this->token_is_valid() ) 832 $this->show_blogs(); 833 else 834 $this->greet(); 835 836 $saved = $this->save_vars(); 837 838 if ( $saved && !isset($_GET['noheader']) ) { 839 $restart = __('Restart'); 840 $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.'); 841 $submit = esc_attr__('Clear account information'); 842 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>"; 843 } 844 } 845 846 function save_vars() { 847 $vars = get_object_vars($this); 848 update_option( 'blogger_importer', $vars ); 849 850 return !empty($vars); 851 } 852 853 function admin_head() { 854 ?> 855 <style type="text/css"> 856 td { text-align: center; line-height: 2em;} 857 thead td { font-weight: bold; } 858 .bar { 859 width: 200px; 860 text-align: left; 861 line-height: 2em; 862 padding: 0px; 863 } 864 .ind { 865 position: absolute; 866 background-color: #83B4D8; 867 width: 1px; 868 z-index: 9; 869 } 870 .stat { 871 z-index: 10; 872 position: relative; 873 text-align: center; 874 } 875 </style> 876 <?php 877 } 878 879 function Blogger_Import() { 880 global $importer_started; 881 $importer_started = time(); 882 if ( isset( $_GET['import'] ) && $_GET['import'] == 'blogger' ) { 883 wp_enqueue_script('jquery'); 884 add_action('admin_head', array(&$this, 'admin_head')); 885 } 886 } 887 } 888 889 $blogger_import = new Blogger_Import(); 890 891 register_importer('blogger', __('Blogger'), __('Import posts, comments, and users from a Blogger blog.'), array ($blogger_import, 'start')); 892 893 class AtomEntry { 894 var $links = array(); 895 var $categories = array(); 896 } 897 898 class AtomParser { 899 900 var $ATOM_CONTENT_ELEMENTS = array('content','summary','title','subtitle','rights'); 901 var $ATOM_SIMPLE_ELEMENTS = array('id','updated','published','draft','author'); 902 903 var $depth = 0; 904 var $indent = 2; 905 var $in_content; 906 var $ns_contexts = array(); 907 var $ns_decls = array(); 908 var $is_xhtml = false; 909 var $skipped_div = false; 910 911 var $entry; 912 913 function AtomParser() { 914 $this->entry = new AtomEntry(); 915 } 916 917 function _map_attrs_func( $k, $v ) { 918 return "$k=\"$v\""; 919 } 920 921 function _map_xmlns_func( $p, $n ) { 922 $xd = "xmlns"; 923 if ( strlen( $n[0] ) > 0 ) 924 $xd .= ":{$n[0]}"; 925 926 return "{$xd}=\"{$n[1]}\""; 927 } 928 929 function parse($xml) { 930 931 global $app_logging; 932 array_unshift($this->ns_contexts, array()); 933 934 $parser = xml_parser_create_ns(); 935 xml_set_object($parser, $this); 936 xml_set_element_handler($parser, "start_element", "end_element"); 937 xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0); 938 xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,0); 939 xml_set_character_data_handler($parser, "cdata"); 940 xml_set_default_handler($parser, "_default"); 941 xml_set_start_namespace_decl_handler($parser, "start_ns"); 942 xml_set_end_namespace_decl_handler($parser, "end_ns"); 943 944 $contents = ""; 945 946 xml_parse($parser, $xml); 947 948 xml_parser_free($parser); 949 950 return true; 951 } 952 953 function start_element($parser, $name, $attrs) { 954 955 $tag = array_pop(split(":", $name)); 956 957 array_unshift($this->ns_contexts, $this->ns_decls); 958 959 $this->depth++; 960 961 if(!empty($this->in_content)) { 962 $attrs_prefix = array(); 963 964 // resolve prefixes for attributes 965 foreach($attrs as $key => $value) { 966 $attrs_prefix[$this->ns_to_prefix($key)] = $this->xml_escape($value); 967 } 968 $attrs_str = join(' ', array_map( array( &$this, '_map_attrs_func' ), array_keys($attrs_prefix), array_values($attrs_prefix))); 969 if(strlen($attrs_str) > 0) { 970 $attrs_str = " " . $attrs_str; 971 } 972 973 $xmlns_str = join(' ', array_map( array( &$this, '_map_xmlns_func' ), array_keys($this->ns_contexts[0]), array_values($this->ns_contexts[0]))); 974 if(strlen($xmlns_str) > 0) { 975 $xmlns_str = " " . $xmlns_str; 976 } 977 978 // handle self-closing tags (case: a new child found right-away, no text node) 979 if(count($this->in_content) == 2) { 980 array_push($this->in_content, ">"); 981 } 982 983 array_push($this->in_content, "<". $this->ns_to_prefix($name) ."{$xmlns_str}{$attrs_str}"); 984 } else if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS) || in_array($tag, $this->ATOM_SIMPLE_ELEMENTS)) { 985 $this->in_content = array(); 986 $this->is_xhtml = $attrs['type'] == 'xhtml'; 987 array_push($this->in_content, array($tag,$this->depth)); 988 } else if($tag == 'link') { 989 array_push($this->entry->links, $attrs); 990 } else if($tag == 'category') { 991 array_push($this->entry->categories, $attrs['term']); 992 } 993 994 $this->ns_decls = array(); 995 } 996 997 function end_element($parser, $name) { 998 999 $tag = array_pop(split(":", $name)); 1000 1001 if(!empty($this->in_content)) { 1002 if($this->in_content[0][0] == $tag && 1003 $this->in_content[0][1] == $this->depth) { 1004 array_shift($this->in_content); 1005 if($this->is_xhtml) { 1006 $this->in_content = array_slice($this->in_content, 2, count($this->in_content)-3); 1007 } 1008 $this->entry->$tag = join('',$this->in_content); 1009 $this->in_content = array(); 1010 } else { 1011 $endtag = $this->ns_to_prefix($name); 1012 if (strpos($this->in_content[count($this->in_content)-1], '<' . $endtag) !== false) { 1013 array_push($this->in_content, "/>"); 1014 } else { 1015 array_push($this->in_content, "</$endtag>"); 1016 } 1017 } 1018 } 1019 1020 array_shift($this->ns_contexts); 1021 1022 #print str_repeat(" ", $this->depth * $this->indent) . "end_element('$name')" ."\n"; 1023 1024 $this->depth--; 1025 } 1026 1027 function start_ns($parser, $prefix, $uri) { 1028 #print str_repeat(" ", $this->depth * $this->indent) . "starting: " . $prefix . ":" . $uri . "\n"; 1029 array_push($this->ns_decls, array($prefix,$uri)); 1030 } 1031 1032 function end_ns($parser, $prefix) { 1033 #print str_repeat(" ", $this->depth * $this->indent) . "ending: #" . $prefix . "#\n"; 1034 } 1035 1036 function cdata($parser, $data) { 1037 #print str_repeat(" ", $this->depth * $this->indent) . "data: #" . $data . "#\n"; 1038 if(!empty($this->in_content)) { 1039 // handle self-closing tags (case: text node found, need to close element started) 1040 if (strpos($this->in_content[count($this->in_content)-1], '<') !== false) { 1041 array_push($this->in_content, ">"); 1042 } 1043 array_push($this->in_content, $this->xml_escape($data)); 1044 } 1045 } 1046 1047 function _default($parser, $data) { 1048 # when does this gets called? 1049 } 1050 1051 1052 function ns_to_prefix($qname) { 1053 $components = split(":", $qname); 1054 $name = array_pop($components); 1055 1056 if(!empty($components)) { 1057 $ns = join(":",$components); 1058 foreach($this->ns_contexts as $context) { 1059 foreach($context as $mapping) { 1060 if($mapping[1] == $ns && strlen($mapping[0]) > 0) { 1061 return "$mapping[0]:$name"; 1062 } 1063 } 1064 } 1065 } 1066 return $name; 1067 } 1068 1069 function xml_escape($string) 1070 { 1071 return str_replace(array('&','"',"'",'<','>'), 1072 array('&','"',''','<','>'), 1073 $string ); 1074 } 1075 } 1076 1077 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Fri Jan 8 00:19:48 2010 | Cross-referenced by PHPXref 0.7 |