Changes from trunk/wp-admin/import/blogger.php at r4679 to branches/2.2/wp-admin/import/blogger.php at r5405
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2/wp-admin/import/blogger.php
r4679 r5405 1 1 <?php 2 2 3 define( 'MAX_RESULTS', 50 ); // How many records per GData query 4 define( 'MAX_EXECUTION_TIME', 20 ); // How many seconds to let the script run 5 define( 'STATUS_INTERVAL', 3 ); // How many seconds between status bar updates 6 3 7 class Blogger_Import { 4 8 5 var $lump_authors = false; 6 var $import = array(); 7 8 // Shows the welcome screen and the magic iframe. 9 // Shows the welcome screen and the magic auth link. 9 10 function greet() { 10 $title = __('Import Old Blogger'); 11 $welcome = __('Howdy! This importer allows you to import posts and comments from your Old Blogger account into your WordPress blog.'); 12 $noiframes = __('This feature requires iframe support.'); 13 $warning = js_escape(__('This will delete everything saved by the Blogger importer except your posts and comments. Are you sure you want to do this?')); 14 $reset = __('Reset this importer'); 15 $incompat = __('Your web server is not properly configured to use this importer. Please enable the CURL extension for PHP and then reload this page.'); 16 17 echo "<div class='wrap'><h2>$title</h2><p>$welcome</p>"; 18 echo "<p>" . __('Please note that this importer <em>does not work with new Blogger (using your Google account)</em>.') . "</p>"; 19 if ( function_exists('curl_init') ) 20 echo "<iframe src='admin.php?import=blogger&noheader=true' height='350px' width = '99%'>$noiframes</iframe><p><a href='admin.php?import=blogger&restart=true&noheader=true' onclick='return confirm(\"$warning\")'>$reset</a></p>"; 11 $next_url = get_option('siteurl') . '/wp-admin/index.php?import=blogger&noheader=true'; 12 $auth_url = "https://www.google.com/accounts/AuthSubRequest"; 13 $title = __('Import Blogger'); 14 $welcome = __('Howdy! This importer allows you to import posts and comments from your Blogger account into your WordPress blog.'); 15 $prereqs = __('To use this importer, you must have a Google account, an upgraded (New, was Beta) blog, and it must be on blogspot or a custom domain (not FTP).'); 16 $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.'); 17 $auth = __('Authorize'); 18 19 echo " 20 <div class='wrap'><h2>$title</h2><p>$welcome</p><p>$prereqs</p><p>$stepone</p> 21 <form action='$auth_url' method='get'> 22 <p class='submit' style='text-align:left;'> 23 <input type='submit' value='$auth' /> 24 <input type='hidden' name='scope' value='http://www.blogger.com/feeds/' /> 25 <input type='hidden' name='session' value='1' /> 26 <input type='hidden' name='secure' value='0' /> 27 <input type='hidden' name='next' value='$next_url' /> 28 </p> 29 </form> 30 </div>\n"; 31 } 32 33 function uh_oh($title, $message, $info) { 34 echo "<div class='wrap'><h2>$title</h2><p>$message</p><pre>$info</pre></div>"; 35 } 36 37 function auth() { 38 // We have a single-use token that must be upgraded to a session token. 39 $token = preg_replace( '/[^-_0-9a-zA-Z]/', '', $_GET['token'] ); 40 $headers = array( 41 "GET /accounts/AuthSubSessionToken HTTP/1.0", 42 "Authorization: AuthSub token=\"$token\"" 43 ); 44 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 45 $sock = $this->_get_auth_sock( ); 46 if ( ! $sock ) return false; 47 $response = $this->_txrx( $sock, $request ); 48 preg_match( '/token=([-_0-9a-z]+)/i', $response, $matches ); 49 if ( empty( $matches[1] ) ) { 50 $this->uh_oh( 51 __( 'Authorization failed' ), 52 __( 'Something went wrong. If the problem persists, send this info to support:' ), 53 htmlspecialchars($response) 54 ); 55 return false; 56 } 57 $this->token = $matches[1]; 58 59 wp_redirect( remove_query_arg( array( 'token', 'noheader' ) ) ); 60 } 61 62 function get_token_info() { 63 $headers = array( 64 "GET /accounts/AuthSubTokenInfo HTTP/1.0", 65 "Authorization: AuthSub token=\"$this->token\"" 66 ); 67 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 68 $sock = $this->_get_auth_sock( ); 69 if ( ! $sock ) return; 70 $response = $this->_txrx( $sock, $request ); 71 return $this->parse_response($response); 72 } 73 74 function token_is_valid() { 75 $info = $this->get_token_info(); 76 77 if ( $info['code'] == 200 ) 78 return true; 79 80 return false; 81 } 82 83 function show_blogs($iter = 0) { 84 if ( empty($this->blogs) ) { 85 $headers = array( 86 "GET /feeds/default/blogs HTTP/1.0", 87 "Host: www2.blogger.com", 88 "Authorization: AuthSub token=\"$this->token\"" 89 ); 90 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 91 $sock = $this->_get_blogger_sock( ); 92 if ( ! $sock ) return; 93 $response = $this->_txrx( $sock, $request ); 94 95 // Quick and dirty XML mining. 96 list( $headers, $xml ) = explode( "\r\n\r\n", $response ); 97 $p = xml_parser_create(); 98 xml_parse_into_struct($p, $xml, $vals, $index); 99 xml_parser_free($p); 100 101 $this->title = $vals[$index['TITLE'][0]]['value']; 102 103 // Give it a few retries... this step often flakes out the first time. 104 if ( empty( $index['ENTRY'] ) ) { 105 if ( $iter < 3 ) { 106 return $this->show_blogs($iter + 1); 107 } else { 108 $this->uh_oh( 109 __('Trouble signing in'), 110 __('We were not able to gain access to your account. Try starting over.'), 111 '' 112 ); 113 return false; 114 } 115 } 116 117 foreach ( $index['ENTRY'] as $i ) { 118 $blog = array(); 119 while ( ( $tag = $vals[$i] ) && ! ( $tag['tag'] == 'ENTRY' && $tag['type'] == 'close' ) ) { 120 if ( $tag['tag'] == 'TITLE' ) { 121 $blog['title'] = $tag['value']; 122 } elseif ( $tag['tag'] == 'SUMMARY' ) { 123 $blog['summary'] == $tag['value']; 124 } elseif ( $tag['tag'] == 'LINK' ) { 125 if ( $tag['attributes']['REL'] == 'alternate' && $tag['attributes']['TYPE'] == 'text/html' ) { 126 $parts = parse_url( $tag['attributes']['HREF'] ); 127 $blog['host'] = $parts['host']; 128 } elseif ( $tag['attributes']['REL'] == 'edit' ) 129 $blog['gateway'] = $tag['attributes']['HREF']; 130 } 131 ++$i; 132 } 133 if ( ! empty ( $blog ) ) { 134 $blog['total_posts'] = $this->get_total_results('posts', $blog['host']); 135 $blog['total_comments'] = $this->get_total_results('comments', $blog['host']); 136 $blog['mode'] = 'init'; 137 $this->blogs[] = $blog; 138 } 139 } 140 141 if ( empty( $this->blogs ) ) { 142 $this->uh_oh( 143 __('No blogs found'), 144 __('We were able to log in but there were no blogs. Try a different account next time.'), 145 '' 146 ); 147 return false; 148 } 149 } 150 //echo '<pre>'.print_r($this,1).'</pre>'; 151 $start = js_escape( __('Import') ); 152 $continue = js_escape( __('Continue') ); 153 $stop = js_escape( __('Importing...') ); 154 $authors = js_escape( __('Set Authors') ); 155 $loadauth = js_escape( __('Preparing author mapping form...') ); 156 $authhead = js_escape( __('Final Step: Author Mapping') ); 157 $nothing = js_escape( __('Nothing was imported. Had you already imported this blog?') ); 158 $title = __('Blogger Blogs'); 159 $name = __('Blog Name'); 160 $url = __('Blog URL'); 161 $action = __('The Magic Button'); 162 $posts = __('Posts'); 163 $comments = __('Comments'); 164 $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.'); 165 166 $interval = STATUS_INTERVAL * 1000; 167 168 foreach ( $this->blogs as $i => $blog ) { 169 if ( $blog['mode'] == 'init' ) 170 $value = $start; 171 elseif ( $blog['mode'] == 'posts' || $blog['mode'] == 'comments' ) 172 $value = $continue; 173 else 174 $value = $authors; 175 $blogtitle = js_escape( $blog['title'] ); 176 $pdone = isset($blog['posts_done']) ? (int) $blog['posts_done'] : 0; 177 $cdone = isset($blog['comments_done']) ? (int) $blog['comments_done'] : 0; 178 $init .= "blogs[$i]=new blog($i,'$blogtitle','{$blog['mode']}'," . $this->get_js_status($i) . ');'; 179 $pstat = "<div class='ind' id='pind$i'> </div><div id='pstat$i' class='stat'>$pdone/{$blog['total_posts']}</div>"; 180 $cstat = "<div class='ind' id='cind$i'> </div><div id='cstat$i' class='stat'>$cdone/{$blog['total_comments']}</div>"; 181 $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' id='submit$i' value='$value' /><input type='hidden' name='blog' value='$i' /></td></tr>\n"; 182 } 183 184 echo "<div class='wrap'><h2>$title</h2><noscript>$noscript</noscript><table cellpadding='5px'><thead><td>$name</td><td>$url</td><td>$posts</td><td>$comments</td><td>$action</td></thead>\n$rows</table></form></div>"; 185 echo " 186 <script type='text/javascript'> 187 var strings = {cont:'$continue',stop:'$stop',stopping:'$stopping',authors:'$authors',nothing:'$nothing'}; 188 var blogs = {}; 189 function blog(i, title, mode, status){ 190 this.blog = i; 191 this.mode = mode; 192 this.title = title; 193 this.status = status; 194 this.button = document.getElementById('submit'+this.blog); 195 }; 196 blog.prototype = { 197 start: function() { 198 this.cont = true; 199 this.kick(); 200 this.check(); 201 }, 202 kick: function() { 203 ++this.kicks; 204 var i = this.blog; 205 jQuery.post('admin.php?import=blogger&noheader=true',{blog:this.blog},function(text,result){blogs[i].kickd(text,result)}); 206 }, 207 check: function() { 208 ++this.checks; 209 var i = this.blog; 210 jQuery.post('admin.php?import=blogger&noheader=true&status=true',{blog:this.blog},function(text,result){blogs[i].checkd(text,result)}); 211 }, 212 kickd: function(text, result) { 213 if ( result == 'error' ) { 214 // TODO: exception handling 215 if ( this.cont ) 216 setTimeout('blogs['+this.blog+'].kick()', 1000); 217 } else { 218 if ( text == 'done' ) { 219 this.stop(); 220 this.done(); 221 } else if ( text == 'nothing' ) { 222 this.stop(); 223 this.nothing(); 224 } else if ( text == 'continue' ) { 225 this.kick(); 226 } else if ( this.mode = 'stopped' ) 227 jQuery(this.button).attr('value', strings.cont); 228 } 229 --this.kicks; 230 }, 231 checkd: function(text, result) { 232 if ( result == 'error' ) { 233 // TODO: exception handling 234 } else { 235 eval('this.status='+text); 236 jQuery('#pstat'+this.blog).empty().append(this.status.p1+'/'+this.status.p2); 237 jQuery('#cstat'+this.blog).empty().append(this.status.c1+'/'+this.status.c2); 238 this.update(); 239 if ( this.cont || this.kicks > 0 ) 240 setTimeout('blogs['+this.blog+'].check()', $interval); 241 } 242 --this.checks; 243 }, 244 update: function() { 245 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'); 246 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'); 247 }, 248 stop: function() { 249 this.cont = false; 250 }, 251 done: function() { 252 this.mode = 'authors'; 253 jQuery(this.button).attr('value', strings.authors); 254 }, 255 nothing: function() { 256 this.mode = 'nothing'; 257 jQuery(this.button).remove(); 258 alert(strings.nothing); 259 }, 260 getauthors: function() { 261 if ( jQuery('div.wrap').length > 1 ) 262 jQuery('div.wrap').gt(0).remove(); 263 jQuery('div.wrap').empty().append('<h2>$authhead</h2><h3>' + this.title + '</h3>'); 264 jQuery('div.wrap').append('<p id=\"auth\">$loadauth</p>'); 265 jQuery('p#auth').load('index.php?import=blogger&noheader=true&authors=1',{blog:this.blog}); 266 }, 267 init: function() { 268 this.update(); 269 var i = this.blog; 270 jQuery(this.button).bind('click', function(){return blogs[i].click();}); 271 this.kicks = 0; 272 this.checks = 0; 273 }, 274 click: function() { 275 if ( this.mode == 'init' || this.mode == 'stopped' || this.mode == 'posts' || this.mode == 'comments' ) { 276 this.mode = 'started'; 277 this.start(); 278 jQuery(this.button).attr('value', strings.stop); 279 } else if ( this.mode == 'started' ) { 280 return false; // let it run... 281 this.mode = 'stopped'; 282 this.stop(); 283 if ( this.checks > 0 || this.kicks > 0 ) { 284 this.mode = 'stopping'; 285 jQuery(this.button).attr('value', strings.stopping); 286 } else { 287 jQuery(this.button).attr('value', strings.cont); 288 } 289 } else if ( this.mode == 'authors' ) { 290 document.location = 'index.php?import=blogger&authors=1&blog='+this.blog; 291 //this.mode = 'authors2'; 292 //this.getauthors(); 293 } 294 return false; 295 } 296 }; 297 $init 298 jQuery.each(blogs, function(i, me){me.init();}); 299 </script>\n"; 300 } 301 302 // Handy function for stopping the script after a number of seconds. 303 function have_time() { 304 global $importer_started; 305 if ( time() - $importer_started > MAX_EXECUTION_TIME ) 306 die('continue'); 307 return true; 308 } 309 310 function get_total_results($type, $host) { 311 $headers = array( 312 "GET /feeds/$type/default?max-results=1&start-index=2 HTTP/1.0", 313 "Host: $host", 314 "Authorization: AuthSub token=\"$this->token\"" 315 ); 316 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 317 $sock = $this->_get_blogger_sock( $host ); 318 if ( ! $sock ) return; 319 $response = $this->_txrx( $sock, $request ); 320 $response = $this->parse_response( $response ); 321 $parser = xml_parser_create(); 322 xml_parse_into_struct($parser, $response['body'], $struct, $index); 323 xml_parser_free($parser); 324 $total_results = $struct[$index['OPENSEARCH:TOTALRESULTS'][0]]['value']; 325 return (int) $total_results; 326 } 327 328 function import_blog($blogID) { 329 global $importing_blog; 330 $importing_blog = $blogID; 331 332 if ( isset($_GET['authors']) ) 333 return print($this->get_author_form()); 334 335 header('Content-Type: text/plain'); 336 337 if ( isset($_GET['status']) ) 338 die($this->get_js_status()); 339 340 if ( isset($_GET['saveauthors']) ) 341 die($this->save_authors()); 342 343 $blog = $this->blogs[$blogID]; 344 $total_results = $this->get_total_results('posts', $blog['host']); 345 $this->blogs[$importing_blog]['total_posts'] = $total_results; 346 347 $start_index = $total_results - MAX_RESULTS + 1; 348 349 if ( isset( $this->blogs[$importing_blog]['posts_start_index'] ) ) 350 $start_index = (int) $this->blogs[$importing_blog]['posts_start_index']; 351 elseif ( $total_results > MAX_RESULTS ) 352 $start_index = $total_results - MAX_RESULTS + 1; 21 353 else 22 echo "<p>$incompat</p>"; 23 echo "</div>\n"; 24 } 25 26 // Deletes saved data and redirect. 354 $start_index = 1; 355 356 // This will be positive until we have finished importing posts 357 if ( $start_index > 0 ) { 358 // Grab all the posts 359 $this->blogs[$importing_blog]['mode'] = 'posts'; 360 $query = "start-index=$start_index&max-results=" . MAX_RESULTS; 361 do { 362 $index = $struct = $entries = array(); 363 $headers = array( 364 "GET /feeds/posts/default?$query HTTP/1.0", 365 "Host: {$blog['host']}", 366 "Authorization: AuthSub token=\"$this->token\"" 367 ); 368 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 369 $sock = $this->_get_blogger_sock( $blog['host'] ); 370 if ( ! $sock ) return; // TODO: Error handling 371 $response = $this->_txrx( $sock, $request ); 372 373 $response = $this->parse_response( $response ); 374 375 // Extract the entries and send for insertion 376 preg_match_all( '/<entry[^>]*>.*?<\/entry>/s', $response['body'], $matches ); 377 if ( count( $matches[0] ) ) { 378 $entries = array_reverse($matches[0]); 379 foreach ( $entries as $entry ) { 380 $entry = "<feed>$entry</feed>"; 381 $AtomParser = new AtomParser(); 382 $AtomParser->parse( $entry ); 383 $this->import_post($AtomParser->entry); 384 unset($AtomParser); 385 } 386 } else break; 387 388 // Get the 'previous' query string which we'll use on the next iteration 389 $query = ''; 390 $links = preg_match_all('/<link([^>]*)>/', $response['body'], $matches); 391 if ( count( $matches[1] ) ) 392 foreach ( $matches[1] as $match ) 393 if ( preg_match('/rel=.previous./', $match) ) 394 $query = html_entity_decode( preg_replace('/^.*href=[\'"].*\?(.+)[\'"].*$/', '$1', $match) ); 395 396 if ( $query ) { 397 parse_str($query, $q); 398 $this->blogs[$importing_blog]['posts_start_index'] = (int) $q['start-index']; 399 } else 400 $this->blogs[$importing_blog]['posts_start_index'] = 0; 401 $this->save_vars(); 402 } while ( !empty( $query ) && $this->have_time() ); 403 } 404 405 $total_results = $this->get_total_results( 'comments', $blog['host'] ); 406 $this->blogs[$importing_blog]['total_comments'] = $total_results; 407 408 if ( isset( $this->blogs[$importing_blog]['comments_start_index'] ) ) 409 $start_index = (int) $this->blogs[$importing_blog]['comments_start_index']; 410 elseif ( $total_results > MAX_RESULTS ) 411 $start_index = $total_results - MAX_RESULTS + 1; 412 else 413 $start_index = 1; 414 415 if ( $start_index > 0 ) { 416 // Grab all the comments 417 $this->blogs[$importing_blog]['mode'] = 'comments'; 418 $query = "start-index=$start_index&max-results=" . MAX_RESULTS; 419 do { 420 $index = $struct = $entries = array(); 421 $headers = array( 422 "GET /feeds/comments/default?$query HTTP/1.0", 423 "Host: {$blog['host']}", 424 "Authorization: AuthSub token=\"$this->token\"" 425 ); 426 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 427 $sock = $this->_get_blogger_sock( $blog['host'] ); 428 if ( ! $sock ) return; // TODO: Error handling 429 $response = $this->_txrx( $sock, $request ); 430 431 $response = $this->parse_response( $response ); 432 433 // Extract the comments and send for insertion 434 preg_match_all( '/<entry[^>]*>.*?<\/entry>/s', $response['body'], $matches ); 435 if ( count( $matches[0] ) ) { 436 $entries = array_reverse( $matches[0] ); 437 foreach ( $entries as $entry ) { 438 $entry = "<feed>$entry</feed>"; 439 $AtomParser = new AtomParser(); 440 $AtomParser->parse( $entry ); 441 $this->import_comment($AtomParser->entry); 442 unset($AtomParser); 443 } 444 } 445 446 // Get the 'previous' query string which we'll use on the next iteration 447 $query = ''; 448 $links = preg_match_all('/<link([^>]*)>/', $response['body'], $matches); 449 if ( count( $matches[1] ) ) 450 foreach ( $matches[1] as $match ) 451 if ( preg_match('/rel=.previous./', $match) ) 452 $query = html_entity_decode( preg_replace('/^.*href=[\'"].*\?(.+)[\'"].*$/', '$1', $match) ); 453 454 parse_str($query, $q); 455 456 $this->blogs[$importing_blog]['comments_start_index'] = (int) $q['start-index']; 457 $this->save_vars(); 458 } while ( !empty( $query ) && $this->have_time() ); 459 } 460 $this->blogs[$importing_blog]['mode'] = 'authors'; 461 $this->save_vars(); 462 if ( !$this->blogs[$importing_blog]['posts_done'] && !$this->blogs[$importing_blog]['comments_done'] ) 463 die('nothing'); 464 do_action('import_done', 'blogger'); 465 die('done'); 466 } 467 468 function convert_date( $date ) { 469 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); 470 $offset = iso8601_timezone_to_offset( $date_bits[7] ); 471 $timestamp = gmmktime($date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1]); 472 $timestamp -= $offset; // Convert from Blogger local time to GMT 473 $timestamp += get_option('gmt_offset') * 3600; // Convert from GMT to WP local time 474 return gmdate('Y-m-d H:i:s', $timestamp); 475 } 476 477 function no_apos( $string ) { 478 return str_replace( ''', "'", $string); 479 } 480 481 function min_whitespace( $string ) { 482 return preg_replace( '|\s+|', ' ', $string ); 483 } 484 485 function import_post( $entry ) { 486 global $wpdb, $importing_blog; 487 488 // The old permalink is all Blogger gives us to link comments to their posts. 489 if ( isset( $entry->draft ) ) 490 $rel = 'self'; 491 else 492 $rel = 'alternate'; 493 foreach ( $entry->links as $link ) { 494 if ( $link['rel'] == $rel ) { 495 $parts = parse_url( $link['href'] ); 496 $entry->old_permalink = $parts['path']; 497 break; 498 } 499 } 500 501 $post_date = $this->convert_date( $entry->published ); 502 $post_content = trim( addslashes( $this->no_apos( html_entity_decode( $entry->content ) ) ) ); 503 $post_title = trim( addslashes( $this->no_apos( $this->min_whitespace( $entry->title ) ) ) ); 504 $post_status = isset( $entry->draft ) ? 'draft' : 'publish'; 505 506 // Clean up content 507 $post_content = preg_replace('|<(/?[A-Z]+)|e', "'<' . strtolower('$1')", $post_content); 508 $post_content = str_replace('<br>', '<br />', $post_content); 509 $post_content = str_replace('<hr>', '<hr />', $post_content); 510 511 // Checks for duplicates 512 if ( 513 isset( $this->blogs[$importing_blog]['posts'][$entry->old_permalink] ) || 514 post_exists( $post_title, $post_content, $post_date ) 515 ) { 516 ++$this->blogs[$importing_blog]['posts_skipped']; 517 } else { 518 $post = compact('post_date', 'post_content', 'post_title', 'post_status'); 519 520 $post_id = wp_insert_post($post); 521 522 wp_create_categories( array_map( 'addslashes', $entry->categories ), $post_id ); 523 524 $author = $this->no_apos( strip_tags( $entry->author ) ); 525 526 add_post_meta( $post_id, 'blogger_blog', $this->blogs[$importing_blog]['host'], true ); 527 add_post_meta( $post_id, 'blogger_author', $author, true ); 528 add_post_meta( $post_id, 'blogger_permalink', $entry->old_permalink, true ); 529 530 $this->blogs[$importing_blog]['posts'][$entry->old_permalink] = $post_id; 531 ++$this->blogs[$importing_blog]['posts_done']; 532 } 533 $this->save_vars(); 534 } 535 536 function import_comment( $entry ) { 537 global $importing_blog; 538 539 // Drop the #fragment and we have the comment's old post permalink. 540 foreach ( $entry->links as $link ) { 541 if ( $link['rel'] == 'alternate' ) { 542 $parts = parse_url( $link['href'] ); 543 $entry->old_permalink = $parts['fragment']; 544 $entry->old_post_permalink = $parts['path']; 545 break; 546 } 547 } 548 549 $comment_post_ID = (int) $this->blogs[$importing_blog]['posts'][$entry->old_post_permalink]; 550 preg_match('#<name>(.+?)</name>.*(?:\<uri>(.+?)</uri>)?#', $entry->author, $matches); 551 $comment_author = addslashes( $this->no_apos( strip_tags( (string) $matches[1] ) ) ); 552 $comment_author_url = addslashes( $this->no_apos( strip_tags( (string) $matches[2] ) ) ); 553 $comment_date = $this->convert_date( $entry->updated ); 554 $comment_content = addslashes( $this->no_apos( html_entity_decode( $entry->content ) ) ); 555 556 // Clean up content 557 $comment_content = preg_replace('|<(/?[A-Z]+)|e', "'<' . strtolower('$1')", $comment_content); 558 $comment_content = str_replace('<br>', '<br />', $comment_content); 559 $comment_content = str_replace('<hr>', '<hr />', $comment_content); 560 561 // Checks for duplicates 562 if ( 563 isset( $this->blogs[$importing_blog]['comments'][$entry->old_permalink] ) || 564 comment_exists( $comment_author, $comment_date ) 565 ) { 566 ++$this->blogs[$importing_blog]['comments_skipped']; 567 } else { 568 $comment = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_date', 'comment_content'); 569 570 $comment_id = wp_insert_comment($comment); 571 572 $this->blogs[$importing_blog]['comments'][$entry->old_permalink] = $comment_id; 573 574 ++$this->blogs[$importing_blog]['comments_done']; 575 } 576 $this->save_vars(); 577 } 578 579 function get_js_status($blog = false) { 580 global $importing_blog; 581 if ( $blog === false ) 582 $blog = $this->blogs[$importing_blog]; 583 else 584 $blog = $this->blogs[$blog]; 585 $p1 = isset( $blog['posts_done'] ) ? (int) $blog['posts_done'] : 0; 586 $p2 = isset( $blog['total_posts'] ) ? (int) $blog['total_posts'] : 0; 587 $c1 = isset( $blog['comments_done'] ) ? (int) $blog['comments_done'] : 0; 588 $c2 = isset( $blog['total_comments'] ) ? (int) $blog['total_comments'] : 0; 589 return "{p1:$p1,p2:$p2,c1:$c1,c2:$c2}"; 590 } 591 592 function get_author_form($blog = false) { 593 global $importing_blog, $wpdb, $current_user; 594 if ( $blog === false ) 595 $blog = & $this->blogs[$importing_blog]; 596 else 597 $blog = & $this->blogs[$blog]; 598 599 if ( !isset( $blog['authors'] ) ) { 600 $post_ids = array_values($blog['posts']); 601 $authors = (array) $wpdb->get_col("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN (" . join( ',', $post_ids ) . ")"); 602 $blog['authors'] = array_map(null, $authors, array_fill(0, count($authors), $current_user->ID)); 603 $this->save_vars(); 604 } 605 606 $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.'); 607 $heading = __('Author mapping'); 608 $blogtitle = "{$blog['title']} ({$blog['host']})"; 609 $mapthis = __('Blogger username'); 610 $tothis = __('WordPress login'); 611 $submit = js_escape( __('Save Changes »') ); 612 613 foreach ( $blog['authors'] as $i => $author ) 614 $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>"; 615 616 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='authorsubmit' value='$submit' /></td></tr></table></form></div>"; 617 } 618 619 function get_user_options($current) { 620 global $wpdb, $importer_users; 621 if ( ! isset( $importer_users ) ) 622 $importer_users = (array) get_users_of_blog(); 623 624 foreach ( $importer_users as $user ) { 625 $sel = ( $user->user_id == $current ) ? " selected='selected'" : ''; 626 $options .= "<option value='$user->user_id'$sel>$user->display_name</option>"; 627 } 628 629 return $options; 630 } 631 632 function save_authors() { 633 global $importing_blog, $wpdb; 634 $authors = (array) $_POST['authors']; 635 636 $host = $this->blogs[$importing_blog]['host']; 637 638 // Get an array of posts => authors 639 $post_ids = (array) $wpdb->get_col("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'blogger_blog' AND meta_value = '$host'"); 640 $post_ids = join( ',', $post_ids ); 641 $results = (array) $wpdb->get_results("SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN ($post_ids)"); 642 foreach ( $results as $row ) 643 $authors_posts[$row->post_id] = $row->meta_value; 644 645 foreach ( $authors as $author => $user_id ) { 646 $user_id = (int) $user_id; 647 648 // Skip authors that haven't been changed 649 if ( $user_id == $this->blogs[$importing_blog]['authors'][$author][1] ) 650 continue; 651 652 // Get a list of the selected author's posts 653 $post_ids = (array) array_keys( $authors_posts, $this->blogs[$importing_blog]['authors'][$author][0] ); 654 $post_ids = join( ',', $post_ids); 655 656 $wpdb->query("UPDATE $wpdb->posts SET post_author = $user_id WHERE id IN ($post_ids)"); 657 $this->blogs[$importing_blog]['authors'][$author][1] = $user_id; 658 } 659 $this->save_vars(); 660 661 wp_redirect('edit.php'); 662 } 663 664 function _get_auth_sock() { 665 // Connect to https://www.google.com 666 if ( !$sock = @ fsockopen('ssl://www.google.com', 443, $errno, $errstr) ) { 667 $this->uh_oh( 668 __('Could not connect to https://www.google.com'), 669 __('There was a problem opening a secure connection to Google. This is what went wrong:'), 670 "$errstr ($errno)" 671 ); 672 return false; 673 } 674 return $sock; 675 } 676 677 function _get_blogger_sock($host = 'www2.blogger.com') { 678 if ( !$sock = @ fsockopen($host, 80, $errno, $errstr) ) { 679 $this->uh_oh( 680 sprintf( __('Could not connect to %s'), $host ), 681 __('There was a problem opening a connection to Blogger. This is what went wrong:'), 682 "$errstr ($errno)" 683 ); 684 return false; 685 } 686 return $sock; 687 } 688 689 function _txrx( $sock, $request ) { 690 fwrite( $sock, $request ); 691 while ( ! feof( $sock ) ) 692 $response .= @ fread ( $sock, 8192 ); 693 fclose( $sock ); 694 return $response; 695 } 696 697 function revoke($token) { 698 $headers = array( 699 "GET /accounts/AuthSubRevokeToken HTTP/1.0", 700 "Authorization: AuthSub token=\"$token\"" 701 ); 702 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 703 $sock = $this->_get_auth_sock( ); 704 if ( ! $sock ) return false; 705 $this->_txrx( $sock, $request ); 706 } 707 27 708 function restart() { 28 delete_option('import-blogger'); 29 wp_redirect("admin.php?import=blogger"); 30 die(); 31 } 32 33 // Generates a string that will make the page reload in a specified interval. 34 function refresher($msec) { 35 if ( $msec ) 36 return "<html><head><script type='text/javascript'>window.onload=setTimeout('window.location.reload()', $msec);</script>\n</head>\n<body>\n"; 37 else 38 return "<html><head><script type='text/javascript'>window.onload=window.location.reload();</script>\n</head>\n<body>\n"; 709 global $wpdb; 710 $options = get_option( 'blogger_importer' ); 711 712 if ( isset( $options['token'] ) ) 713 $this->revoke( $options['token'] ); 714 715 delete_option('blogger_importer'); 716 $wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = 'blogger_author'"); 717 wp_redirect('?import=blogger'); 39 718 } 40 719 … … 63 742 } 64 743 65 // Prints a form for the user to enter Blogger creds.66 function login_form($text='') {67 echo '<h1>' . __('Log in to Blogger') . "</h1>\n$text\n";68 echo '<form method="post" action="admin.php?import=blogger&noheader=true&step=0"><table><tr><td>' . __('Username') . ':</td><td><input type="text" name="user" /></td></tr><tr><td>' . __('Password') . ':</td><td><input type="password" name="pass" /></td><td><input type="submit" value="' . __('Start') . '" /></td></tr></table></form>';69 die;70 }71 72 // Sends creds to Blogger, returns the session cookies an array of headers.73 function login_blogger($user, $pass) {74 $_url = 'http://www.blogger.com/login.do';75 $params = "username=$user&password=$pass";76 $ch = curl_init();77 curl_setopt($ch, CURLOPT_POST,1);78 curl_setopt($ch, CURLOPT_POSTFIELDS,$params);79 curl_setopt($ch, CURLOPT_URL,$_url);80 curl_setopt($ch, CURLOPT_USERAGENT, 'Blogger Exporter');81 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);82 curl_setopt($ch, CURLOPT_HEADER,1);83 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);84 $response = curl_exec ($ch);85 86 $response = $this->parse_response($response);87 88 sleep(1);89 90 return $response['cookies'];91 }92 93 // Requests page from Blogger, returns the response array.94 function get_blogger($url, $header = '', $user=false, $pass=false) {95 $ch = curl_init();96 if ($user && $pass) curl_setopt($ch, CURLOPT_USERPWD,"{$user}:{$pass}");97 curl_setopt($ch, CURLOPT_URL,$url);98 curl_setopt($ch, CURLOPT_TIMEOUT, 10);99 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);100 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);101 curl_setopt($ch, CURLOPT_USERAGENT, 'Blogger Exporter');102 curl_setopt($ch, CURLOPT_HEADER,1);103 if (is_array($header)) curl_setopt($ch, CURLOPT_HTTPHEADER, $header);104 $response = curl_exec ($ch);105 106 $response = $this->parse_response($response);107 $response['url'] = $url;108 109 if (curl_errno($ch)) {110 print curl_error($ch);111 } else {112 curl_close($ch);113 }114 115 return $response;116 }117 118 // Posts data to Blogger, returns response array.119 function post_blogger($url, $header = false, $paramary = false, $parse=true) {120 $params = '';121 if ( is_array($paramary) ) {122 foreach($paramary as $key=>$value)123 if($key && $value != '')124 $params.=$key."=".urlencode(stripslashes($value))."&";125 }126 if ($user && $pass) $params .= "username=$user&password=$pass";127 $params = trim($params,'&');128 $ch = curl_init();129 curl_setopt($ch, CURLOPT_POST,1);130 curl_setopt($ch, CURLOPT_POSTFIELDS,$params);131 if ($user && $pass) curl_setopt($ch, CURLOPT_USERPWD,"{$user}:{$pass}");132 curl_setopt($ch, CURLOPT_URL,$url);133 curl_setopt($ch, CURLOPT_USERAGENT, 'Blogger Exporter');134 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);135 curl_setopt($ch, CURLOPT_HEADER,$parse);136 curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);137 if ($header) curl_setopt($ch, CURLOPT_HTTPHEADER, $header);138 $response = curl_exec ($ch);139 140 if ($parse) {141 $response = $this->parse_response($response);142 $response['url'] = $url;143 return $response;144 }145 146 return $response;147 }148 149 // Prints the list of blogs for import.150 function show_blogs() {151 global $import;152 echo '<h1>' . __('Selecting a Blog') . "</h1>\n<ul>";153 foreach ( $this->import['blogs'] as $blog ) {154 if (9 == $blog['nextstep']) $status = "100%";155 elseif (8 == $blog['nextstep']) $status = "90%";156 elseif (7 == $blog['nextstep']) $status = "82.5%";157 elseif (6 == $blog['nextstep']) $status = "75%";158 elseif (5 == $blog['nextstep']) $status = "57%";159 elseif (4 == $blog['nextstep']) $status = "28%";160 elseif (3 == $blog['nextstep']) $status = "14%";161 else $status = "0%";162 echo "\t<li><a href='admin.php?import=blogger&noheader=true&blog={$blog['id']}'>{$blog['title']}</a> $status</li>\n";163 }164 die("</ul>\n");165 }166 167 // Publishes.168 function publish_blogger($i, $text) {169 $head = $this->refresher(2000) . "<h1>$text</h1>\n";170 if ( ! strstr($this->import['blogs'][$_GET['blog']]['publish'][$i], 'http') ) {171 // First call. Start the publish process with a fresh set of cookies.172 $this->import['cookies'] = $this->login_blogger($this->import['user'], $this->import['pass']);173 update_option('import-blogger', $this->import);174 $paramary = array('blogID' => $_GET['blog'], 'all' => '1', 'republishAll' => 'Republish Entire Blog', 'publish' => '1', 'redirectUrl' => "/publish.do?blogID={$_GET['blog']}&inprogress=true");175 176 $response = $this->post_blogger("http://www.blogger.com/publish.do?blogID={$_GET['blog']}", $this->import['cookies'], $paramary);177 if ( $response['code'] == '302' ) {178 $url = str_replace('publish.g', 'publish-body.g', $response['header']['Location']);179 $this->import['blogs'][$_GET['blog']]['publish'][$i] = $url;180 update_option('import-blogger', $this->import);181 $response = $this->get_blogger($url, $this->import['cookies']);182 preg_match('#<p class="progressIndicator">.*</p>#U', $response['body'], $matches);183 $progress = $matches[0];184 die($head . $progress);185 } else {186 $this->import['blogs'][$_GET['blog']]['publish'][$i] = false;187 update_option('import-blogger', $this->import);188 die($head);189 }190 } else {191 // Subsequent call. Keep checking status until Blogger reports publish complete.192 $url = $this->import['blogs'][$_GET['blog']]['publish'][$i];193 $response = $this->get_blogger($url, $this->import['cookies']);194 if ( preg_match('#<p class="progressIndicator">.*</p>#U', $response['body'], $matches) ) {195 $progress = $matches[0];196 if ( strstr($progress, '100%') ) {197 $this->set_next_step($i);198 $progress .= '<p>'.__('Moving on...').'</p>';199 }200 die($head . $progress);201 } else {202 $this->import['blogs'][$_GET['blog']]['publish'][$i] = false;203 update_option('import-blogger', $this->import);204 die("$head<p>" . __('Trying again...') . '</p>');205 }206 }207 }208 209 // Sets next step, saves options210 function set_next_step($step) {211 $this->import['blogs'][$_GET['blog']]['nextstep'] = $step;212 update_option('import-blogger', $this->import);213 }214 215 // Redirects to next step216 function do_next_step() {217 wp_redirect("admin.php?import=blogger&noheader=true&blog={$_GET['blog']}");218 die();219 }220 221 // Step 0: Do Blogger login, get blogid/title pairs.222 function do_login() {223 if ( ( ! $this->import['user'] && ! is_array($this->import['cookies']) ) ) {224 // The user must provide a Blogger username and password.225 if ( ! ( $_POST['user'] && $_POST['pass'] ) ) {226 $this->login_form(__('The script will log into your Blogger account, change some settings so it can read your blog, and restore the original settings when it\'s done. Here\'s what you do:').'</p><ol><li>'.__('Back up your Blogger template.').'</li><li>'.__('Back up any other Blogger settings you might need later.').'</li><li>'.__('Log out of Blogger').'</li><li>'.__('Log in <em>here</em> with your Blogger username and password.').'</li><li>'.__('On the next screen, click one of your Blogger blogs.').'</li><li>'.__('Do not close this window or navigate away until the process is complete.').'</li></ol>');227 }228 229 // Try logging in. If we get an array of cookies back, we at least connected.230 $this->import['cookies'] = $this->login_blogger($_POST['user'], $_POST['pass']);231 if ( !is_array( $this->import['cookies'] ) ) {232 $this->login_form(__('Login failed. Please enter your credentials again.'));233 }234 235 // Save the password so we can log the browser in when it's time to publish.236 $this->import['pass'] = $_POST['pass'];237 $this->import['user'] = $_POST['user'];238 239 // Get the Blogger welcome page and scrape the blog numbers and names from it240 $response = $this->get_blogger('http://www.blogger.com/home', $this->import['cookies']);241 if (! stristr($response['body'], 'signed in as') ) $this->login_form(__('Login failed. Please re-enter your username and password.'));242 $blogsary = array();243 preg_match_all('#posts\.g\?blogID=(\d+)">([^<]+)</a>#U', $response['body'], $blogsary);244 if ( ! count( $blogsary[1] < 1 ) )245 wp_die(__('No blogs found for this user.'));246 $this->import['blogs'] = array();247 $template = '<MainPage><br /><br /><br /><p>'.__('Are you looking for %title%? It is temporarily out of service. Please try again in a few minutes. Meanwhile, discover <a href="http://wordpress.org/">a better blogging tool</a>.').'</p><BloggerArchives><a class="archive" href="<$BlogArchiveURL$>"><$BlogArchiveName$></a><br /></BloggerArchives></MainPage><ArchivePage><Blogger><wordpresspost><$BlogItemDateTime$>|W|P|<$BlogItemAuthorNickname$>|W|P|<$BlogItemBody$>|W|P|<$BlogItemNumber$>|W|P|<$BlogItemTitle$>|W|P|<$BlogItemAuthorEmail$><BlogItemCommentsEnabled><BlogItemComments><wordpresscomment><$BlogCommentDateTime$>|W|P|<$BlogCommentAuthor$>|W|P|<$BlogCommentBody$></BlogItemComments></BlogItemCommentsEnabled></Blogger></ArchivePage>';248 foreach ( $blogsary[1] as $key => $id ) {249 // Define the required Blogger options.250 $blog_opts = array(251 'blog-options-basic' => false,252 'blog-options-archiving' => array('archiveFrequency' => 'm'),253 'blog-publishing' => array('publishMode'=>'0', 'blogID' => "$id", 'subdomain' => mt_rand().mt_rand(), 'pingWeblogs' => 'false'),254 'blog-formatting' => array('timeStampFormat' => '0', 'encoding'=>'UTF-8', 'convertLineBreaks'=>'false', 'floatAlignment'=>'false'),255 'blog-comments' => array('commentsTimeStampFormat' => '0'),256 'template-edit' => array( 'templateText' => str_replace('%title%', trim($blogsary[2][$key]), $template) )257 );258 259 // Build the blog options array template260 foreach ($blog_opts as $blog_opt => $modify)261 $new_opts["$blog_opt"] = array('backup'=>false, 'modify' => $modify, 'error'=>false);262 263 $this->import['blogs']["$id"] = array(264 'id' => $id,265 'title' => trim($blogsary[2][$key]),266 'options' => $new_opts,267 'url' => false,268 'publish_cookies' => false,269 'published' => false,270 'archives' => false,271 'lump_authors' => false,272 'newusers' => array(),273 'nextstep' => 2274 );275 }276 update_option('import-blogger', $this->import);277 wp_redirect("admin.php?import=blogger&noheader=true&step=1");278 }279 die();280 }281 282 // Step 1: Select one of the blogs belonging to the user logged in.283 function select_blog() {284 if ( is_array($this->import['blogs']) ) {285 $this->show_blogs();286 die();287 } else {288 $this->restart();289 }290 }291 292 // Step 2: Backup the Blogger options pages, updating some of them.293 function backup_settings() {294 $output.= '<h1>'.__('Backing up Blogger options')."</h1>\n";295 $form = false;296 foreach ($this->import['blogs'][$_GET['blog']]['options'] as $blog_opt => $optary) {297 if ( $blog_opt == $_GET['form'] ) {298 // Save the posted form data299 $this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['backup'] = $_POST;300 update_option('import-blogger',$this->import);301 302 // Post the modified form data to Blogger303 if ( $optary['modify'] ) {304 $posturl = "http://www.blogger.com/{$blog_opt}.do";305 $headers = array_merge($this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['cookies'], $this->import['cookies']);306 if ( 'blog-publishing' == $blog_opt ) {307 if ( $_POST['publishMode'] > 0 ) {308 $response = $this->get_blogger("http://www.blogger.com/blog-publishing.g?blogID={$_GET['blog']}&publishMode=0", $headers);309 if ( $response['code'] >= 400 )310 wp_die('<h2>'.__('Failed attempt to change publish mode from FTP to BlogSpot.').'</h2><pre>' . addslashes(print_r($headers, 1)) . addslashes(print_r($response, 1)) . '</pre>');311 $this->import['blogs'][$_GET['blog']]['url'] = 'http://' . $optary['modify']['subdomain'] . '.blogspot.com/';312 sleep(2);313 } else {314 $this->import['blogs'][$_GET['blog']]['url'] = 'http://' . $_POST['subdomain'] . '.blogspot.com/';315 update_option('import-blogger', $this->import);316 $output .= "<del><p>$blog_opt</p></del>\n";317 continue;318 }319 $paramary = $optary['modify'];320 } else {321 $paramary = array_merge($_POST, $optary['modify']);322 }323 $response = $this->post_blogger($posturl, $headers, $paramary);324 if ( $response['code'] >= 400 || strstr($response['body'], 'There are errors on this form') )325 wp_die('<p>'.__('Error on form submission. Retry or reset the importer.').'</p>' . addslashes(print_r($response, 1)));326 }327 $output .= "<del><p>$blog_opt</p></del>\n";328 } elseif ( is_array($this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['backup']) ) {329 // This option set has already been backed up.330 $output .= "<del><p>$blog_opt</p></del>\n";331 } elseif ( ! $form ) {332 // This option page needs to be downloaded and given to the browser for submission back to this script.333 $response = $this->get_blogger("http://www.blogger.com/{$blog_opt}.g?blogID={$_GET['blog']}", $this->import['cookies']);334 $this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['cookies'] = $response['cookies'];335 update_option('import-blogger',$this->import);336 $body = $response['body'];337 $body = preg_replace("|\<!DOCTYPE.*\<body[^>]*>|ms","",$body);338 $body = preg_replace("|/?{$blog_opt}.do|","admin.php?import=blogger&noheader=true&step=2&blog={$_GET['blog']}&form={$blog_opt}",$body);339 $body = str_replace("name='submit'","name='supermit'",$body);340 $body = str_replace('name="submit"','name="supermit"',$body);341 $body = str_replace('</body>','',str_replace('</html>','',$body));342 $form = "<div style='height:0px;width:0px;overflow:hidden;'>";343 $form.= $body;344 $form.= "</div><script type='text/javascript'>forms=document.getElementsByTagName('form');for(i=0;i<forms.length;i++){if(forms[i].action.search('{$blog_opt}')){forms[i].submit();break;}}</script>";345 $output.= '<p>'.sprintf(__('<strong>%s</strong> in progress, please wait...'), $blog_opt)."</p>\n";346 } else {347 $output.= "<p>$blog_opt</p>\n";348 }349 }350 if ( $form )351 die($output . $form);352 353 $this->set_next_step(4);354 $this->do_next_step();355 }356 357 // Step 3: Cancelled :-)358 359 // Step 4: Publish with the new template and settings.360 function publish_blog() {361 $this->publish_blogger(5, __('Publishing with new template and options'));362 }363 364 // Step 5: Get the archive URLs from the new blog.365 function get_archive_urls() {366 $bloghtml = $this->get_blogger($this->import['blogs'][$_GET['blog']]['url']);367 if (! strstr($bloghtml['body'], '<a class="archive"') )368 wp_die(__('Your Blogger blog did not take the new template or did not respond.'));369 preg_match_all('#<a class="archive" href="([^"]*)"#', $bloghtml['body'], $archives);370 foreach ($archives[1] as $archive) {371 $this->import['blogs'][$_GET['blog']]['archives'][$archive] = false;372 }373 $this->set_next_step(6);374 $this->do_next_step();375 }376 377 // Step 6: Get each monthly archive, import it, mark it done.378 function get_archive() {379 global $wpdb;380 $output = '<h2>'.__('Importing Blogger archives into WordPress').'</h2>';381 $did_one = false;382 $post_array = $posts = array();383 foreach ( $this->import['blogs'][$_GET['blog']]['archives'] as $url => $status ) {384 $archivename = substr(basename($url),0,7);385 if ( $status || $did_one ) {386 $foo = 'bar';387 // Do nothing.388 } else {389 // Import the selected month390 $postcount = 0;391 $skippedpostcount = 0;392 $commentcount = 0;393 $skippedcommentcount = 0;394 $status = __('in progress...');395 $this->import['blogs'][$_GET['blog']]['archives']["$url"] = $status;396 update_option('import-blogger', $import);397 $archive = $this->get_blogger($url);398 if ( $archive['code'] > 200 )399 continue;400 $posts = explode('<wordpresspost>', $archive['body']);401 for ($i = 1; $i < count($posts); $i = $i + 1) {402 $postparts = explode('<wordpresscomment>', $posts[$i]);403 $postinfo = explode('|W|P|', $postparts[0]);404 $post_date = $postinfo[0];405 $post_content = $postinfo[2];406 // Don't try to re-use the original numbers407 // because the new, longer numbers are too408 // big to handle as ints.409 //$post_number = $postinfo[3];410 $post_title = ( $postinfo[4] != '' ) ? $postinfo[4] : $postinfo[3];411 $post_author_name = $wpdb->escape(trim($postinfo[1]));412 $post_author_email = $postinfo[5] ? $postinfo[5] : 'user@wordpress.org';413 414 if ( $this->lump_authors ) {415 // Ignore Blogger authors. Use the current user_ID for all posts imported.416 $post_author = $GLOBALS['user_ID'];417 } else {418 // Add a user for each new author encountered.419 if (! username_exists($post_author_name) ) {420 $user_login = $wpdb->escape($post_author_name);421 $user_email = $wpdb->escape($post_author_email);422 $user_password = substr(md5(uniqid(microtime())), 0, 6);423 $result = wp_create_user( $user_login, $user_password, $user_email );424 $status.= sprintf(__('Registered user <strong>%s</strong>.'), $user_login);425 $this->import['blogs'][$_GET['blog']]['newusers'][] = $user_login;426 }427 $userdata = get_userdatabylogin( $post_author_name );428 $post_author = $userdata->ID;429 }430 $post_date = explode(' ', $post_date);431 $post_date_Ymd = explode('/', $post_date[0]);432 $postyear = $post_date_Ymd[2];433 $postmonth = zeroise($post_date_Ymd[0], 2);434 $postday = zeroise($post_date_Ymd[1], 2);435 $post_date_His = explode(':', $post_date[1]);436 $posthour = zeroise($post_date_His[0], 2);437 $postminute = zeroise($post_date_His[1], 2);438 $postsecond = zeroise($post_date_His[2], 2);439 440 if (($post_date[2] == 'PM') && ($posthour != '12'))441 $posthour = $posthour + 12;442 else if (($post_date[2] == 'AM') && ($posthour == '12'))443 $posthour = '00';444 445 $post_date = "$postyear-$postmonth-$postday $posthour:$postminute:$postsecond";446 447 $post_content = addslashes($post_content);448 $post_content = str_replace(array('<br>','<BR>','<br/>','<BR/>','<br />','<BR />'), "\n", $post_content); // the XHTML touch... ;)449 450 $post_title = addslashes($post_title);451 452 $post_status = 'publish';453 454 if ( $ID = post_exists($post_title, '', $post_date) ) {455 $post_array[$i]['ID'] = $ID;456 $skippedpostcount++;457 } else {458 $post_array[$i]['post'] = compact('post_author', 'post_content', 'post_title', 'post_category', 'post_author', 'post_date', 'post_status');459 $post_array[$i]['comments'] = false;460 }461 462 // Import any comments attached to this post.463 if ($postparts[1]) :464 for ($j = 1; $j < count($postparts); $j = $j + 1) {465 $commentinfo = explode('|W|P|', $postparts[$j]);466 $comment_date = explode(' ', $commentinfo[0]);467 $comment_date_Ymd = explode('/', $comment_date[0]);468 $commentyear = $comment_date_Ymd[2];469 $commentmonth = zeroise($comment_date_Ymd[0], 2);470 $commentday = zeroise($comment_date_Ymd[1], 2);471 $comment_date_His = explode(':', $comment_date[1]);472 $commenthour = zeroise($comment_date_His[0], 2);473 $commentminute = zeroise($comment_date_His[1], 2);474 $commentsecond = '00';475 if (($comment_date[2] == 'PM') && ($commenthour != '12'))476 $commenthour = $commenthour + 12;477 else if (($comment_date[2] == 'AM') && ($commenthour == '12'))478 $commenthour = '00';479 $comment_date = "$commentyear-$commentmonth-$commentday $commenthour:$commentminute:$commentsecond";480 $comment_author = addslashes(strip_tags($commentinfo[1]));481 if ( strpos($commentinfo[1], 'a href') ) {482 $comment_author_parts = explode('"', htmlentities($commentinfo[1]));483 $comment_author_url = $comment_author_parts[1];484 } else $comment_author_url = '';485 $comment_content = $commentinfo[2];486 $comment_content = str_replace(array('<br>','<BR>','<br/>','<BR/>','<br />','<BR />'), "\n", $comment_content);487 $comment_approved = 1;488 if ( comment_exists($comment_author, $comment_date) ) {489 $skippedcommentcount++;490 } else {491 $comment = compact('comment_author', 'comment_author_url', 'comment_date', 'comment_content', 'comment_approved');492 $post_array[$i]['comments'][$j] = wp_filter_comment($comment);493 }494 $commentcount++;495 }496 endif;497 $postcount++;498 }499 if ( count($post_array) ) {500 krsort($post_array);501 foreach($post_array as $post) {502 if ( ! $comment_post_ID = $post['ID'] )503 $comment_post_ID = wp_insert_post($post['post']);504 if ( $post['comments'] ) {505 foreach ( $post['comments'] as $comment ) {506 $comment['comment_post_ID'] = $comment_post_ID;507 wp_insert_comment($comment);508 }509 }510 }511 }512 $status = sprintf(__('%s post(s) parsed, %s skipped...'), $postcount, $skippedpostcount).' '.513 sprintf(__('%s comment(s) parsed, %s skipped...'), $commentcount, $skippedcommentcount).' '.514 ' <strong>'.__('Done').'</strong>';515 $import = $this->import;516 $import['blogs'][$_GET['blog']]['archives']["$url"] = $status;517 update_option('import-blogger', $import);518 $did_one = true;519 }520 $output.= "<p>$archivename $status</p>\n";521 }522 if ( ! $did_one )523 $this->set_next_step(7);524 die( $this->refresher(1000) . $output );525 }526 527 // Step 7: Restore the backed-up settings to Blogger528 function restore_settings() {529 $output = '<h1>'.__('Restoring your Blogger options')."</h1>\n";530 $did_one = false;531 // Restore options in reverse order.532 if ( ! $this->import['reversed'] ) {533 $this->import['blogs'][$_GET['blog']]['options'] = array_reverse($this->import['blogs'][$_GET['blog']]['options'], true);534 $this->import['reversed'] = true;535 update_option('import-blogger', $this->import);536 }537 foreach ( $this->import['blogs'][$_GET['blog']]['options'] as $blog_opt => $optary ) {538 if ( $did_one ) {539 $output .= "<p>$blog_opt</p>\n";540 } elseif ( $optary['restored'] || ! $optary['modify'] ) {541 $output .= "<p><del>$blog_opt</del></p>\n";542 } else {543 $posturl = "http://www.blogger.com/{$blog_opt}.do";544 $headers = array_merge($this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['cookies'], $this->import['cookies']);545 if ( 'blog-publishing' == $blog_opt) {546 if ( $optary['backup']['publishMode'] > 0 ) {547 $response = $this->get_blogger("http://www.blogger.com/blog-publishing.g?blogID={$_GET['blog']}&publishMode={$optary['backup']['publishMode']}", $headers);548 sleep(2);549 if ( $response['code'] >= 400 )550 wp_die('<h1>'.__('Error restoring publishMode').'</h1><p>'.__('Please tell the devs.').'</p>' . addslashes(print_r($response, 1)) );551 }552 }553 if ( $optary['backup'] != $optary['modify'] ) {554 $response = $this->post_blogger($posturl, $headers, $optary['backup']);555 if ( $response['code'] >= 400 || strstr($response['body'], 'There are errors on this form') ) {556 $this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['error'] = true;557 update_option('import-blogger', $this->import);558 $output .= sprintf(__('%s failed. Trying again.'), "<p><strong>$blog_opt</strong> ").'</p>';559 } else {560 $this->import['blogs'][$_GET['blog']]['options']["$blog_opt"]['restored'] = true;561 update_option('import-blogger', $this->import);562 $output .= sprintf(__('%s restored.'), "<p><strong>$blog_opt</strong> ").'</p>';563 }564 }565 $did_one = true;566 }567 }568 569 if ( $did_one ) {570 die( $this->refresher(1000) . $output );571 } elseif ( $this->import['blogs'][$_GET['blog']]['options']['blog-publishing']['backup']['publishMode'] > 0 ) {572 $this->set_next_step(9);573 } else {574 $this->set_next_step(8);575 }576 577 $this->do_next_step();578 }579 580 // Step 8: Republish, all back to normal581 function republish_blog() {582 $this->publish_blogger(9, __('Publishing with original template and options'));583 }584 585 744 // Step 9: Congratulate the user 586 745 function congrats() { 746 $blog = (int) $_GET['blog']; 587 747 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>'; 588 748 if ( count($this->import['blogs']) > 1 ) 589 749 echo '<li>'.__('In case you haven\'t done it already, you can import the posts from your other blogs:'). $this->show_blogs() . '</li>'; 590 if ( $n = count($this->import['blogs'][$ _GET['blog']]['newusers']) )750 if ( $n = count($this->import['blogs'][$blog]['newusers']) ) 591 751 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>'; 592 echo '<li>'.__('For security, click the link below to reset this importer. That will clear your Blogger credentials and options from the database.').'</li>';752 echo '<li>'.__('For security, click the link below to reset this importer.').'</li>'; 593 753 echo '</ul>'; 594 754 } … … 596 756 // Figures out what to do, then does it. 597 757 function start() { 598 if ( $_GET['restart'] == 'true' ) {758 if ( isset($_POST['restart']) ) 599 759 $this->restart(); 600 } 601 602 if ( isset($_GET['noheader']) ) { 603 header('Content-Type: text/html; charset=utf-8'); 604 605 $this->import = get_option('import-blogger'); 606 607 if ( false === $this->import ) { 608 $step = 0; 609 } elseif ( isset($_GET['step']) ) { 610 $step = (int) $_GET['step']; 611 } elseif ( isset($_GET['blog']) && isset($this->import['blogs'][$_GET['blog']]['nextstep']) ) { 612 $step = $this->import['blogs'][$_GET['blog']]['nextstep']; 613 } elseif ( is_array($this->import['blogs']) ) { 614 $step = 1; 760 761 $options = get_option('blogger_importer'); 762 763 if ( is_array($options) ) 764 foreach ( $options as $key => $value ) 765 $this->$key = $value; 766 767 if ( isset( $_REQUEST['blog'] ) ) { 768 $blog = is_array($_REQUEST['blog']) ? array_shift( array_keys( $_REQUEST['blog'] ) ) : $_REQUEST['blog']; 769 $blog = (int) $blog; 770 $this->import_blog( $blog ); 771 } elseif ( isset($_GET['token']) ) 772 $this->auth(); 773 elseif ( $this->token && $this->token_is_valid() ) 774 $this->show_blogs(); 775 else 776 $this->greet(); 777 778 $saved = $this->save_vars(); 779 780 if ( $saved && !isset($_GET['noheader']) ) { 781 $restart = __('Restart'); 782 $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.'); 783 $submit = __('Clear account information'); 784 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' value='$submit' name='restart' /></p></form></div>"; 785 } 786 } 787 788 function save_vars() { 789 $vars = get_object_vars($this); 790 update_option( 'blogger_importer', $vars ); 791 792 return !empty($vars); 793 } 794 795 function admin_head() { 796 ?> 797 <style type="text/css"> 798 td { text-align: center; line-height: 2em;} 799 thead td { font-weight: bold; } 800 .bar { 801 width: 200px; 802 text-align: left; 803 line-height: 2em; 804 padding: 0px; 805 } 806 .ind { 807 position: absolute; 808 background-color: #83B4D8; 809 width: 1px; 810 z-index: 9; 811 } 812 .stat { 813 z-index: 10; 814 position: relative; 815 text-align: center; 816 } 817 .submit { 818 text-align: center !important; 819 } 820 </style> 821 <?php 822 } 823 824 function Blogger_Import() { 825 global $importer_started; 826 $importer_started = time(); 827 if ( isset( $_GET['import'] ) && $_GET['import'] == 'blogger' ) { 828 wp_enqueue_script('jquery'); 829 add_action('admin_head', array(&$this, 'admin_head')); 830 } 831 } 832 } 833 834 $blogger_import = new Blogger_Import(); 835 836 register_importer('blogger', __('Blogger'), __('Import posts, comments, and users from a Blogger blog'), array ($blogger_import, 'start')); 837 838 class AtomEntry { 839 var $links = array(); 840 var $categories = array(); 841 } 842 843 class AtomParser { 844 845 var $ATOM_CONTENT_ELEMENTS = array('content','summary','title','subtitle','rights'); 846 var $ATOM_SIMPLE_ELEMENTS = array('id','updated','published','draft','author'); 847 848 var $depth = 0; 849 var $indent = 2; 850 var $in_content; 851 var $ns_contexts = array(); 852 var $ns_decls = array(); 853 var $is_xhtml = false; 854 var $skipped_div = false; 855 856 var $entry; 857 858 function AtomParser() { 859 860 $this->entry = new AtomEntry(); 861 $this->map_attrs_func = create_function('$k,$v', 'return "$k=\"$v\"";'); 862 $this->map_xmlns_func = create_function('$p,$n', '$xd = "xmlns"; if(strlen($n[0])>0) $xd .= ":{$n[0]}"; return "{$xd}=\"{$n[1]}\"";'); 863 } 864 865 function parse($xml) { 866 867 global $app_logging; 868 array_unshift($this->ns_contexts, array()); 869 870 $parser = xml_parser_create_ns(); 871 xml_set_object($parser, $this); 872 xml_set_element_handler($parser, "start_element", "end_element"); 873 xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0); 874 xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,0); 875 xml_set_character_data_handler($parser, "cdata"); 876 xml_set_default_handler($parser, "_default"); 877 xml_set_start_namespace_decl_handler($parser, "start_ns"); 878 xml_set_end_namespace_decl_handler($parser, "end_ns"); 879 880 $contents = ""; 881 882 xml_parse($parser, $xml); 883 884 xml_parser_free($parser); 885 886 return true; 887 } 888 889 function start_element($parser, $name, $attrs) { 890 891 $tag = array_pop(split(":", $name)); 892 893 array_unshift($this->ns_contexts, $this->ns_decls); 894 895 $this->depth++; 896 897 if(!empty($this->in_content)) { 898 $attrs_prefix = array(); 899 900 // resolve prefixes for attributes 901 foreach($attrs as $key => $value) { 902 $attrs_prefix[$this->ns_to_prefix($key)] = $this->xml_escape($value); 903 } 904 $attrs_str = join(' ', array_map($this->map_attrs_func, array_keys($attrs_prefix), array_values($attrs_prefix))); 905 if(strlen($attrs_str) > 0) { 906 $attrs_str = " " . $attrs_str; 907 } 908 909 $xmlns_str = join(' ', array_map($this->map_xmlns_func, array_keys($this->ns_contexts[0]), array_values($this->ns_contexts[0]))); 910 if(strlen($xmlns_str) > 0) { 911 $xmlns_str = " " . $xmlns_str; 912 } 913 914 // handle self-closing tags (case: a new child found right-away, no text node) 915 if(count($this->in_content) == 2) { 916 array_push($this->in_content, ">"); 917 } 918 919 array_push($this->in_content, "<". $this->ns_to_prefix($name) ."{$xmlns_str}{$attrs_str}"); 920 } else if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS) || in_array($tag, $this->ATOM_SIMPLE_ELEMENTS)) { 921 $this->in_content = array(); 922 $this->is_xhtml = $attrs['type'] == 'xhtml'; 923 array_push($this->in_content, array($tag,$this->depth)); 924 } else if($tag == 'link') { 925 array_push($this->entry->links, $attrs); 926 } else if($tag == 'category') { 927 array_push($this->entry->categories, $attrs['term']); 928 } 929 930 $this->ns_decls = array(); 931 } 932 933 function end_element($parser, $name) { 934 935 $tag = array_pop(split(":", $name)); 936 937 if(!empty($this->in_content)) { 938 if($this->in_content[0][0] == $tag && 939 $this->in_content[0][1] == $this->depth) { 940 array_shift($this->in_content); 941 if($this->is_xhtml) { 942 $this->in_content = array_slice($this->in_content, 2, count($this->in_content)-3); 943 } 944 $this->entry->$tag = join('',$this->in_content); 945 $this->in_content = array(); 615 946 } else { 616 $step = 0; 617 } 618 //echo "Step $step."; 619 //wp_die('<pre>'.print_r($this->import,1).'</pre'); 620 switch ($step) { 621 case 0 : 622 $this->do_login(); 623 break; 624 case 1 : 625 $this->select_blog(); 626 break; 627 case 2 : 628 $this->backup_settings(); 629 break; 630 case 3 : 631 $this->wait_for_blogger(); 632 break; 633 case 4 : 634 $this->publish_blog(); 635 break; 636 case 5 : 637 $this->get_archive_urls(); 638 break; 639 case 6 : 640 $this->get_archive(); 641 break; 642 case 7 : 643 $this->restore_settings(); 644 break; 645 case 8 : 646 $this->republish_blog(); 647 break; 648 case 9 : 649 $this->congrats(); 650 break; 651 } 652 die; 653 654 } else { 655 $this->greet(); 656 } 657 } 658 659 function Blogger_Import() { 660 // This space intentionally left blank. 947 $endtag = $this->ns_to_prefix($name); 948 if (strpos($this->in_content[count($this->in_content)-1], '<' . $endtag) !== false) { 949 array_push($this->in_content, "/>"); 950 } else { 951 array_push($this->in_content, "</$endtag>"); 952 } 953 } 954 } 955 956 array_shift($this->ns_contexts); 957 958 #print str_repeat(" ", $this->depth * $this->indent) . "end_element('$name')" ."\n"; 959 960 $this->depth--; 961 } 962 963 function start_ns($parser, $prefix, $uri) { 964 #print str_repeat(" ", $this->depth * $this->indent) . "starting: " . $prefix . ":" . $uri . "\n"; 965 array_push($this->ns_decls, array($prefix,$uri)); 966 } 967 968 function end_ns($parser, $prefix) { 969 #print str_repeat(" ", $this->depth * $this->indent) . "ending: #" . $prefix . "#\n"; 970 } 971 972 function cdata($parser, $data) { 973 #print str_repeat(" ", $this->depth * $this->indent) . "data: #" . $data . "#\n"; 974 if(!empty($this->in_content)) { 975 // handle self-closing tags (case: text node found, need to close element started) 976 if (strpos($this->in_content[count($this->in_content)-1], '<') !== false) { 977 array_push($this->in_content, ">"); 978 } 979 array_push($this->in_content, $this->xml_escape($data)); 980 } 981 } 982 983 function _default($parser, $data) { 984 # when does this gets called? 985 } 986 987 988 function ns_to_prefix($qname) { 989 $components = split(":", $qname); 990 $name = array_pop($components); 991 992 if(!empty($components)) { 993 $ns = join(":",$components); 994 foreach($this->ns_contexts as $context) { 995 foreach($context as $mapping) { 996 if($mapping[1] == $ns && strlen($mapping[0]) > 0) { 997 return "$mapping[0]:$name"; 998 } 999 } 1000 } 1001 } 1002 return $name; 1003 } 1004 1005 function xml_escape($string) 1006 { 1007 return str_replace(array('&','"',"'",'<','>'), 1008 array('&','"',''','<','>'), 1009 $string ); 661 1010 } 662 1011 } 663 1012 664 $blogger_import = new Blogger_Import();665 666 register_importer('blogger', __('Old Blogger'), __('Import posts, comments, and users from an Old Blogger blog'), array ($blogger_import, 'start'));667 668 1013 ?>
Note: See TracChangeset
for help on using the changeset viewer.