Blogger Importer - Version 0.3

Version Description

  • Bugfix for 403 Invalid AuthSub Token http://core.trac.wordpress.org/ticket/14629

=

Download this release

Release Info

Developer ryan
Plugin Icon 128x128 Blogger Importer
Version 0.3
Comparing to
See all releases

Version 0.3

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