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