PHP Cross Reference of WordPress Subversion HEAD

[ Index ]     [ Classes ]     [ Functions ]     [ Variables ]     [ Constants ]

title

Body

[close]

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

   1  <?php
   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  
   7  class Blogger_Import {
   8  
   9      // Shows the welcome screen and the magic auth link.
  10  	function greet() {
  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'>&nbsp;</div><div id='pstat$i' class='stat'>$pdone/{$blog['total_posts']}</div>";
 180              $cstat = "<div class='ind' id='cind$i'>&nbsp;</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;
 353          else
 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                          $result = $this->import_post($AtomParser->entry);
 384                          if ( is_wp_error( $result ) ) 
 385                              return $result;
 386                          unset($AtomParser);
 387                      }
 388                  } else break;
 389  
 390                  // Get the 'previous' query string which we'll use on the next iteration
 391                  $query = '';
 392                  $links = preg_match_all('/<link([^>]*)>/', $response['body'], $matches);
 393                  if ( count( $matches[1] ) )
 394                      foreach ( $matches[1] as $match )
 395                          if ( preg_match('/rel=.previous./', $match) )
 396                              $query = html_entity_decode( preg_replace('/^.*href=[\'"].*\?(.+)[\'"].*$/', '$1', $match) );
 397  
 398                  if ( $query ) {
 399                      parse_str($query, $q);
 400                      $this->blogs[$importing_blog]['posts_start_index'] = (int) $q['start-index'];
 401                  } else
 402                      $this->blogs[$importing_blog]['posts_start_index'] = 0;
 403                  $this->save_vars();
 404              } while ( !empty( $query ) && $this->have_time() );
 405          }
 406  
 407          $total_results = $this->get_total_results( 'comments', $blog['host'] );
 408          $this->blogs[$importing_blog]['total_comments'] = $total_results;
 409  
 410          if ( isset( $this->blogs[$importing_blog]['comments_start_index'] ) )
 411              $start_index = (int) $this->blogs[$importing_blog]['comments_start_index'];
 412          elseif ( $total_results > MAX_RESULTS )
 413              $start_index = $total_results - MAX_RESULTS + 1;
 414          else
 415              $start_index = 1;
 416  
 417          if ( $start_index > 0 ) {
 418              // Grab all the comments
 419              $this->blogs[$importing_blog]['mode'] = 'comments';
 420              $query = "start-index=$start_index&max-results=" . MAX_RESULTS;
 421              do {
 422                  $index = $struct = $entries = array();
 423                  $headers = array(
 424                      "GET /feeds/comments/default?$query HTTP/1.0",
 425                      "Host: {$blog['host']}",
 426                      "Authorization: AuthSub token=\"$this->token\""
 427                  );
 428                  $request = join( "\r\n", $headers ) . "\r\n\r\n";
 429                  $sock = $this->_get_blogger_sock( $blog['host'] );
 430                  if ( ! $sock ) return; // TODO: Error handling
 431                  $response = $this->_txrx( $sock, $request );
 432  
 433                  $response = $this->parse_response( $response );
 434  
 435                  // Extract the comments and send for insertion
 436                  preg_match_all( '/<entry[^>]*>.*?<\/entry>/s', $response['body'], $matches );
 437                  if ( count( $matches[0] ) ) {
 438                      $entries = array_reverse( $matches[0] );
 439                      foreach ( $entries as $entry ) {
 440                          $entry = "<feed>$entry</feed>";
 441                          $AtomParser = new AtomParser();
 442                          $AtomParser->parse( $entry );
 443                          $this->import_comment($AtomParser->entry);
 444                          unset($AtomParser);
 445                      }
 446                  }
 447  
 448                  // Get the 'previous' query string which we'll use on the next iteration
 449                  $query = '';
 450                  $links = preg_match_all('/<link([^>]*)>/', $response['body'], $matches);
 451                  if ( count( $matches[1] ) )
 452                      foreach ( $matches[1] as $match )
 453                          if (