[ Index ]

PHP Cross Reference of Wordpress 2.9.1

title

Body

[close]

/wp-admin/import/ -> blogger.php (source)

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


Generated: Fri Jan 8 00:19:48 2010 Cross-referenced by PHPXref 0.7