Changeset 4896
- Timestamp:
- 02/20/2007 07:46:40 AM (18 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/wp-admin/import/blogger.php
r4679 r4896 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: www.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 = $this->blogs[$importing_blog]['posts'][$entry->old_post_permalink]; 550 $comment_author = addslashes( $this->no_apos( strip_tags( $entry->author ) ) ); 551 $comment_date = $this->convert_date( $entry->updated ); 552 $comment_content = addslashes( $this->no_apos( html_entity_decode( $entry->content ) ) ); 553 554 // Clean up content 555 $comment_content = preg_replace('|<(/?[A-Z]+)|e', "'<' . strtolower('$1')", $comment_content); 556 $comment_content = str_replace('<br>', '<br />', $comment_content); 557 $comment_content = str_replace('<hr>', '<hr />', $comment_content); 558 559 // Checks for duplicates 560 if ( 561 isset( $this->blogs[$importing_blog]['comments'][$entry->old_permalink] ) || 562 comment_exists( $comment_author, $comment_date ) 563 ) { 564 ++$this->blogs[$importing_blog]['comments_skipped']; 565 } else { 566 $comment = compact('comment_post_ID', 'comment_author', 'comment_date', 'comment_content'); 567 568 $comment_id = wp_insert_comment($comment); 569 570 $this->blogs[$importing_blog]['comments'][$entry->old_permalink] = $comment_id; 571 572 ++$this->blogs[$importing_blog]['comments_done']; 573 } 574 $this->save_vars(); 575 } 576 577 function get_js_status($blog = false) { 578 global $importing_blog; 579 if ( $blog === false ) 580 $blog = $this->blogs[$importing_blog]; 581 else 582 $blog = $this->blogs[$blog]; 583 $p1 = isset( $blog['posts_done'] ) ? (int) $blog['posts_done'] : 0; 584 $p2 = isset( $blog['total_posts'] ) ? (int) $blog['total_posts'] : 0; 585 $c1 = isset( $blog['comments_done'] ) ? (int) $blog['comments_done'] : 0; 586 $c2 = isset( $blog['total_comments'] ) ? (int) $blog['total_comments'] : 0; 587 return "{p1:$p1,p2:$p2,c1:$c1,c2:$c2}"; 588 } 589 590 function get_author_form($blog = false) { 591 global $importing_blog, $wpdb, $current_user; 592 if ( $blog === false ) 593 $blog = & $this->blogs[$importing_blog]; 594 else 595 $blog = & $this->blogs[$blog]; 596 597 if ( !isset( $blog['authors'] ) ) { 598 $post_ids = array_values($blog['posts']); 599 $authors = (array) $wpdb->get_col("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN (" . join( ',', $post_ids ) . ")"); 600 $blog['authors'] = array_map(null, $authors, array_fill(0, count($authors), $current_user->ID)); 601 $this->save_vars(); 602 } 603 604 $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.'); 605 $heading = __('Author mapping'); 606 $blogtitle = "{$blog['title']} ({$blog['host']})"; 607 $mapthis = __('Blogger username'); 608 $tothis = __('WordPress login'); 609 $submit = js_escape( __('Save Changes »') ); 610 611 foreach ( $blog['authors'] as $i => $author ) 612 $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>"; 613 614 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>"; 615 } 616 617 function get_user_options($current) { 618 global $wpdb, $importer_users; 619 if ( ! isset( $importer_users ) ) 620 $importer_users = (array) get_users_of_blog(); 621 622 foreach ( $importer_users as $user ) { 623 $sel = ( $user->user_id == $current ) ? " selected='selected'" : ''; 624 $options .= "<option value='$user->user_id'$sel>$user->display_name</option>"; 625 } 626 627 return $options; 628 } 629 630 function save_authors() { 631 global $importing_blog, $wpdb; 632 $authors = (array) $_POST['authors']; 633 634 $host = $this->blogs[$importing_blog]['host']; 635 636 // Get an array of posts => authors 637 $post_ids = (array) $wpdb->get_col("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'blogger_blog' AND meta_value = '$host'"); 638 $post_ids = join( ',', $post_ids ); 639 $results = (array) $wpdb->get_results("SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN ($post_ids)"); 640 foreach ( $results as $row ) 641 $authors_posts[$row->post_id] = $row->meta_value; 642 643 foreach ( $authors as $author => $user_id ) { 644 $user_id = (int) $user_id; 645 646 // Skip authors that haven't been changed 647 if ( $user_id == $this->blogs[$importing_blog]['authors'][$author][1] ) 648 continue; 649 650 // Get a list of the selected author's posts 651 $post_ids = (array) array_keys( $authors_posts, $this->blogs[$importing_blog]['authors'][$author][0] ); 652 $post_ids = join( ',', $post_ids); 653 654 $wpdb->query("UPDATE $wpdb->posts SET post_author = $user_id WHERE id IN ($post_ids)"); 655 $this->blogs[$importing_blog]['authors'][$author][1] = $user_id; 656 } 657 $this->save_vars(); 658 659 wp_redirect('edit.php'); 660 } 661 662 function _get_auth_sock() { 663 // Connect to https://www.google.com 664 if ( !$sock = @ fsockopen('ssl://www.google.com', 443, $errno, $errstr) ) { 665 $this->uh_oh( 666 __('Could not connect to https://www.google.com'), 667 __('There was a problem opening a secure connection to Google. This is what went wrong:'), 668 "$errstr ($errno)" 669 ); 670 return false; 671 } 672 return $sock; 673 } 674 675 function _get_blogger_sock($host = 'www.blogger.com') { 676 if ( !$sock = @ fsockopen($host, 80, $errno, $errstr) ) { 677 $this->uh_oh( 678 sprintf( __('Could not connect to %s'), $host ), 679 __('There was a problem opening a connection to Blogger. This is what went wrong:'), 680 "$errstr ($errno)" 681 ); 682 return false; 683 } 684 return $sock; 685 } 686 687 function _txrx( $sock, $request ) { 688 fwrite( $sock, $request ); 689 while ( ! feof( $sock ) ) 690 $response .= @ fread ( $sock, 8192 ); 691 fclose( $sock ); 692 return $response; 693 } 694 695 function revoke($token) { 696 $headers = array( 697 "GET /accounts/AuthSubRevokeToken HTTP/1.0", 698 "Authorization: AuthSub token=\"$token\"" 699 ); 700 $request = join( "\r\n", $headers ) . "\r\n\r\n"; 701 $sock = $this->_get_auth_sock( ); 702 if ( ! $sock ) return false; 703 $this->_txrx( $sock, $request ); 704 } 705 27 706 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"; 707 global $wpdb; 708 $options = get_option( 'blogger_importer' ); 709 710 if ( isset( $options['token'] ) ) 711 $this->revoke( $options['token'] ); 712 713 delete_option('blogger_importer'); 714 $wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = 'blogger_author'"); 715 wp_redirect('?import=blogger'); 39 716 } 40 717 … … 61 738 62 739 return array("code" => $response_code, "header" => $response_header_array, "cookies" => $cookie_array, "body" => $response_body); 63 }64 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 740 } 584 741 … … 590 747 if ( $n = count($this->import['blogs'][$_GET['blog']]['newusers']) ) 591 748 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>';749 echo '<li>'.__('For security, click the link below to reset this importer.').'</li>'; 593 750 echo '</ul>'; 594 751 } … … 596 753 // Figures out what to do, then does it. 597 754 function start() { 598 if ( $_GET['restart'] == 'true' ) {755 if ( isset($_POST['restart']) ) 599 756 $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; 757 758 $options = get_option('blogger_importer'); 759 760 if ( is_array($options) ) 761 foreach ( $options as $key => $value ) 762 $this->$key = $value; 763 764 if ( isset( $_REQUEST['blog'] ) ) { 765 $blog = is_array($_REQUEST['blog']) ? array_shift( array_keys( $_REQUEST['blog'] ) ) : $_REQUEST['blog']; 766 $this->import_blog( $blog ); 767 } elseif ( isset($_GET['token']) ) 768 $this->auth(); 769 elseif ( $this->token && $this->token_is_valid() ) 770 $this->show_blogs(); 771 else 772 $this->greet(); 773 774 $saved = $this->save_vars(); 775 776 if ( $saved && !isset($_GET['noheader']) ) { 777 $restart = __('Restart'); 778 $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.'); 779 $submit = __('Clear account information'); 780 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>"; 781 } 782 } 783 784 function save_vars() { 785 $vars = get_object_vars($this); 786 update_option( 'blogger_importer', $vars ); 787 788 return !empty($vars); 789 } 790 791 function admin_head() { 792 ?> 793 <style type="text/css"> 794 td { text-align: center; line-height: 2em;} 795 thead td { font-weight: bold; } 796 .bar { 797 width: 200px; 798 text-align: left; 799 line-height: 2em; 800 padding: 0px; 801 } 802 .ind { 803 position: absolute; 804 background-color: #83B4D8; 805 width: 1px; 806 z-index: 9; 807 } 808 .stat { 809 z-index: 10; 810 position: relative; 811 text-align: center; 812 } 813 .submit { 814 text-align: center !important; 815 } 816 </style> 817 <?php 818 } 819 820 function Blogger_Import() { 821 global $importer_started; 822 $importer_started = time(); 823 if ( isset( $_GET['import'] ) && $_GET['import'] == 'blogger' ) { 824 wp_enqueue_script('jquery'); 825 add_action('admin_head', array(&$this, 'admin_head')); 826 } 827 } 828 } 829 830 $blogger_import = new Blogger_Import(); 831 832 register_importer('blogger', __('Blogger'), __('Import posts, comments, and users from a Blogger blog'), array ($blogger_import, 'start')); 833 834 class AtomEntry { 835 var $links = array(); 836 var $categories = array(); 837 } 838 839 class AtomParser { 840 841 var $ATOM_CONTENT_ELEMENTS = array('content','summary','title','subtitle','rights'); 842 var $ATOM_SIMPLE_ELEMENTS = array('id','updated','published','draft','author'); 843 844 var $depth = 0; 845 var $indent = 2; 846 var $in_content; 847 var $ns_contexts = array(); 848 var $ns_decls = array(); 849 var $is_xhtml = false; 850 var $skipped_div = false; 851 852 var $entry; 853 854 function AtomParser() { 855 856 $this->entry = new AtomEntry(); 857 $this->map_attrs_func = create_function('$k,$v', 'return "$k=\"$v\"";'); 858 $this->map_xmlns_func = create_function('$p,$n', '$xd = "xmlns"; if(strlen($n[0])>0) $xd .= ":{$n[0]}"; return "{$xd}=\"{$n[1]}\"";'); 859 } 860 861 function parse($xml) { 862 863 global $app_logging; 864 array_unshift($this->ns_contexts, array()); 865 866 $parser = xml_parser_create_ns(); 867 xml_set_object($parser, $this); 868 xml_set_element_handler($parser, "start_element", "end_element"); 869 xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0); 870 xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,0); 871 xml_set_character_data_handler($parser, "cdata"); 872 xml_set_default_handler($parser, "_default"); 873 xml_set_start_namespace_decl_handler($parser, "start_ns"); 874 xml_set_end_namespace_decl_handler($parser, "end_ns"); 875 876 $contents = ""; 877 878 xml_parse($parser, $xml); 879 880 xml_parser_free($parser); 881 882 return true; 883 } 884 885 function start_element($parser, $name, $attrs) { 886 887 $tag = array_pop(split(":", $name)); 888 889 array_unshift($this->ns_contexts, $this->ns_decls); 890 891 $this->depth++; 892 893 if(!empty($this->in_content)) { 894 $attrs_prefix = array(); 895 896 // resolve prefixes for attributes 897 foreach($attrs as $key => $value) { 898 $attrs_prefix[$this->ns_to_prefix($key)] = $this->xml_escape($value); 899 } 900 $attrs_str = join(' ', array_map($this->map_attrs_func, array_keys($attrs_prefix), array_values($attrs_prefix))); 901 if(strlen($attrs_str) > 0) { 902 $attrs_str = " " . $attrs_str; 903 } 904 905 $xmlns_str = join(' ', array_map($this->map_xmlns_func, array_keys($this->ns_contexts[0]), array_values($this->ns_contexts[0]))); 906 if(strlen($xmlns_str) > 0) { 907 $xmlns_str = " " . $xmlns_str; 908 } 909 910 // handle self-closing tags (case: a new child found right-away, no text node) 911 if(count($this->in_content) == 2) { 912 array_push($this->in_content, ">"); 913 } 914 915 array_push($this->in_content, "<". $this->ns_to_prefix($name) ."{$xmlns_str}{$attrs_str}"); 916 } else if(in_array($tag, $this->ATOM_CONTENT_ELEMENTS) || in_array($tag, $this->ATOM_SIMPLE_ELEMENTS)) { 917 $this->in_content = array(); 918 $this->is_xhtml = $attrs['type'] == 'xhtml'; 919 array_push($this->in_content, array($tag,$this->depth)); 920 } else if($tag == 'link') { 921 array_push($this->entry->links, $attrs); 922 } else if($tag == 'category') { 923 array_push($this->entry->categories, $attrs['term']); 924 } 925 926 $this->ns_decls = array(); 927 } 928 929 function end_element($parser, $name) { 930 931 $tag = array_pop(split(":", $name)); 932 933 if(!empty($this->in_content)) { 934 if($this->in_content[0][0] == $tag && 935 $this->in_content[0][1] == $this->depth) { 936 array_shift($this->in_content); 937 if($this->is_xhtml) { 938 $this->in_content = array_slice($this->in_content, 2, count($this->in_content)-3); 939 } 940 $this->entry->$tag = join('',$this->in_content); 941 $this->in_content = array(); 615 942 } 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. 943 $endtag = $this->ns_to_prefix($name); 944 if(strstr($this->in_content[count($this->in_content)-1], "<$endtag")) { 945 array_push($this->in_content, "/>"); 946 } else { 947 array_push($this->in_content, "</$endtag>"); 948 } 949 } 950 } 951 952 array_shift($this->ns_contexts); 953 954 #print str_repeat(" ", $this->depth * $this->indent) . "end_element('$name')" ."\n"; 955 956 $this->depth--; 957 } 958 959 function start_ns($parser, $prefix, $uri) { 960 #print str_repeat(" ", $this->depth * $this->indent) . "starting: " . $prefix . ":" . $uri . "\n"; 961 array_push($this->ns_decls, array($prefix,$uri)); 962 } 963 964 function end_ns($parser, $prefix) { 965 #print str_repeat(" ", $this->depth * $this->indent) . "ending: #" . $prefix . "#\n"; 966 } 967 968 function cdata($parser, $data) { 969 #print str_repeat(" ", $this->depth * $this->indent) . "data: #" . $data . "#\n"; 970 if(!empty($this->in_content)) { 971 // handle self-closing tags (case: text node found, need to close element started) 972 if(strstr($this->in_content[count($this->in_content)-1], "<")) { 973 array_push($this->in_content, ">"); 974 } 975 array_push($this->in_content, $this->xml_escape($data)); 976 } 977 } 978 979 function _default($parser, $data) { 980 # when does this gets called? 981 } 982 983 984 function ns_to_prefix($qname) { 985 $components = split(":", $qname); 986 $name = array_pop($components); 987 988 if(!empty($components)) { 989 $ns = join(":",$components); 990 foreach($this->ns_contexts as $context) { 991 foreach($context as $mapping) { 992 if($mapping[1] == $ns && strlen($mapping[0]) > 0) { 993 return "$mapping[0]:$name"; 994 } 995 } 996 } 997 } 998 return $name; 999 } 1000 1001 function xml_escape($string) 1002 { 1003 return str_replace(array('&','"',"'",'<','>'), 1004 array('&','"',''','<','>'), 1005 $string ); 661 1006 } 662 1007 } 663 1008 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 1009 ?>
Note: See TracChangeset
for help on using the changeset viewer.