Blogger Importer - Version 0.7

Version Description

  • Fixed issue with drafts not being imported in the right state
  • Added extra error handling for get_oauth_link to stop blank tokens being sent to the form
  • Restructured code to keep similar steps in single function and to allow testing of components to be done
  • Re-incorporated the "congrats" function and provided a sensible list of what to do next
  • Add a geo_public flag to posts with geotags
  • Dropped _normalize_tag after confirming that it's handled by SimplePie
  • Added image handling http://core.trac.wordpress.org/ticket/4010
  • Added setting author on images
  • Added error handling in get_oauth_link() as suggested by daniel_henrique ref http://core.trac.wordpress.org/ticket/21163
  • Added a check for OpenSSL as suggested by digitalsensus
  • Fixed issue with SimplePie santizer not getting set in Wordpress 3.5
  • Added filter for the congrats function 'blogger_importer_congrats' so other plugins can add in new options
  • Converted manual HTML table to WP_LIST_TABLE
  • Moved inline Javascript to separate file to aid debugging and testing
  • Wrapped data sent to Javascript in I18n functions.
  • Fixed timeout error in the Javascript, timeouts were not being used.
  • Supress post revisions when importing so that DB does not grow
  • Added processing of internal links
  • Added uninstall.php to remove options on uninstall
  • Added a timeout value to all of the wp_remote_get calls as people have reported timeout issues
  • Added a setting to control the large images downloaded from blogger.
  • Stopped logging all the post and comment IDs in arrays and storing in option this improved the importing of very large blogs
  • Fixed issue with comment_author_IP notice
  • Code restructuring to use classes for blog objects
  • Changed AJAX calls to use technique described here http://codex.wordpress.org/AJAX_in_Plugins#Ajax_on_the_Administration_Side
  • Added AdminURL to the greet function rather than hardcoded path
  • Defaulted to turn off post pingbacks
  • Fix to stop it counting pingbacks, issue reported by realdoublebee
  • Retrofitted Security enhancement from 0.6, nonce added to form buttons on main screen
  • Security enhancement, nonce added to form button on authors screen
  • Updated POT file
  • Greek Translation from Stergatou Eleni http://buddypress.org/community/members/lenasterg/
Download this release

Release Info

Developer Otto42
Plugin Icon 128x128 Blogger Importer
Version 0.7
Comparing to
See all releases

Code changes from version 0.6 to 0.7

blogger-entry.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @author Andy Clark
5
+ * @copyright 2013
6
+ * A data object representing the data to be added into Wordpress
7
+ */
8
+
9
+ if (!class_exists('BloggerEntry'))
10
+ {
11
+ class BloggerEntry
12
+ {
13
+ var $links = array();
14
+ var $categories = array();
15
+ var $blogurl = '';
16
+
17
+ function parselinks() {
18
+ foreach ($this->links as $link) {
19
+ // save the self link as meta
20
+ if ($link['rel'] == 'self')
21
+ {
22
+ $postself = $link['href'];
23
+ $parts = parse_url($link['href']);
24
+ $this->old_permalink = $parts['path'];
25
+ }
26
+
27
+ // get the old URI for the page when available
28
+ if ($link['rel'] == 'alternate')
29
+ {
30
+ $parts = parse_url($link['href']);
31
+ $this->bookmark = $parts['path'];
32
+ }
33
+
34
+ // save the replies feed link as meta (ignore the comment form one)
35
+ if ($link['rel'] == 'replies' && false === strpos($link['href'], '#comment-form'))
36
+ {
37
+ $this->postreplies = $link['href'];
38
+ }
39
+ }
40
+ }
41
+
42
+ function import() {
43
+
44
+ $post_date = $this->published;
45
+ $post_content = $this->content;
46
+ $post_title = $this->title;
47
+ $post_status = $this->isDraft ? 'draft' : 'publish';
48
+ //AGC:24/10/2013 Turn off the pingbacks
49
+ $post_pingback = Blogger_Import::POST_PINGBACK;
50
+
51
+ // N.B. Clean up of $post_content is now part of the sanitize class
52
+ // Check for duplication part of calling function
53
+ $post = compact('post_date', 'post_content', 'post_title', 'post_status', 'post_pingback');
54
+
55
+ $post_id = wp_insert_post($post);
56
+ if (is_wp_error($post_id))
57
+ return $post_id;
58
+
59
+ wp_create_categories(array_map('addslashes', $this->categories), $post_id);
60
+
61
+ add_post_meta($post_id, 'blogger_blog', $this->blogurl, true);
62
+ add_post_meta($post_id, 'blogger_author', $this->author, true);
63
+
64
+ if (!$this->isDraft && isset($this->bookmark))
65
+ add_post_meta($post_id, 'blogger_permalink', $this->bookmark, true);
66
+
67
+ add_post_meta($post_id, 'blogger_internal', $this->old_permalink, true);
68
+
69
+ if (isset($this->geotags)) {
70
+ add_post_meta($post_id,'geo_latitude',$this->geotags['geo_latitude']);
71
+ add_post_meta($post_id,'geo_longitude',$this->geotags['geo_longitude']);
72
+ add_post_meta($post_id,'geo_public',1);
73
+ if (isset($this->geotags['geo_address'])) {
74
+ add_post_meta($post_id,'geo_address',$this->geotags['geo_address']);
75
+ }
76
+ }
77
+
78
+ return $post_id;
79
+ }
80
+
81
+
82
+ function post_exists() {
83
+ $p = $this->get_post_by_oldID($this->old_permalink);
84
+
85
+ if ($p == 0 && isset($this->bookmark)) {
86
+ $p = $this->get_post_by_oldID($this->bookmark);
87
+ }
88
+ return $p;
89
+ }
90
+
91
+ function get_post_by_oldID($oldID) {
92
+ //Check to see if this post has been loaded already
93
+ //Can we use get_posts for this?
94
+ global $wpdb;
95
+ $query = "SELECT post_id FROM $wpdb->postmeta m inner join $wpdb->posts p on p.ID = m.post_id where meta_key = 'blogger_internal' and meta_value = '%s' and p.post_type = 'post' LIMIT 0 , 1";
96
+ $p = (int) $wpdb->get_var( $wpdb->prepare($query, $oldID) );
97
+ return $p;
98
+ }
99
+ }
100
+ }
101
+
102
+
103
+ ?>
blogger-importer-blog.php ADDED
@@ -0,0 +1,811 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @author Andy Clark
5
+ * @copyright 2013
6
+ * The overall blog, stats and methods for importing
7
+ */
8
+
9
+ class Blogger_Importer_Blog
10
+ {
11
+ var $ID;
12
+ var $comments_url;
13
+
14
+ function init_defaults($totalposts,$totalcomments)
15
+ {
16
+ $this->total_posts = $totalposts;
17
+ $this->total_comments = $totalcomments;
18
+
19
+ $this->posts_done = $this->count_imported_posts();
20
+ $this->comments_done = $this->count_imported_comments();
21
+
22
+ //Initialise other vars
23
+ $this->posts_skipped = 0;
24
+ $this->comments_skipped = 0;
25
+
26
+ $this->images_done = $this->count_imported_images();
27
+ $this->images_progress = 0;
28
+ $this->images_skipped = 0;
29
+ $this->links_done = 0; //Can't count these as we don't store them in separately in the DB
30
+ $this->links_progress = 0;
31
+
32
+ $this->mode = 'init';
33
+ }
34
+
35
+ //Return the blog from the options or false for failure
36
+ static function read_option($blogID)
37
+ {
38
+ $blog = false;
39
+ $blogopt = get_option('blogger_importer_blog_'.$blogID);
40
+ if (is_array($blogopt)) {
41
+ $blog = new Blogger_Importer_Blog();
42
+ foreach ($blogopt as $key => $value)
43
+ $blog->$key = $value;
44
+ }
45
+ return $blog;
46
+ }
47
+
48
+ function import_blog($connector)
49
+ {
50
+ //AGC: Moved the parameter parsing out to start and the importing into separate functions.
51
+ //25/1/2013 Suppress post revisions whilst we are importing
52
+ remove_post_type_support('post', 'revisions');
53
+
54
+ $this->importer_started = time();
55
+
56
+ if (!$this->import_posts($connector))
57
+ return ('continue');
58
+ if (!$this->import_comments($connector))
59
+ return ('continue');
60
+
61
+ if (Blogger_Import::IMPORT_IMG)
62
+ {
63
+ if (!$this->process_images())
64
+ return('continue');
65
+ }
66
+
67
+ if (!$this->process_links())
68
+ return('continue');
69
+
70
+ $this->mode = 'authors';
71
+ $this->save_vars();
72
+
73
+ if (!$this->posts_done && !$this->comments_done)
74
+ return('nothing');
75
+
76
+ //Thought: Should this be passing an ID so we know which blog just got imported.
77
+ do_action('import_done', 'blogger');
78
+
79
+ return('done');
80
+ }
81
+
82
+
83
+ function import_posts($connector)
84
+ {
85
+ //Simpler counting for posts as we load them forwards
86
+ if (isset($this->posts_start_index))
87
+ $start_index = (int)$this->posts_start_index;
88
+ else
89
+ $start_index = 1;
90
+
91
+ if ($this->total_posts > $start_index)
92
+ {
93
+ // Grab all the posts
94
+ $this->mode = 'posts';
95
+ do
96
+ {
97
+ $index = $struct = $entries = array();
98
+
99
+ $url = $this->posts_url;
100
+
101
+ //Thought, how do we decouple this method from the source?
102
+ //Perhaps the "get data function" can somehow be passed through as a parameter to this.
103
+ //This decoupling would then allow an easier re-code for say when the blogger data arrives as a file not as a URL
104
+ $response = $connector->oauth_get($url, array('max-results' => Blogger_Import::MAX_RESULTS, 'start-index' => $start_index));
105
+
106
+ if ($response == false)
107
+ break;
108
+
109
+ // parse the feed
110
+ $feed = new SimplePie();
111
+
112
+ //set_xxxx methods depreciated (and not working?) replaced with get_registry as per docs
113
+ $reg = $feed->get_registry();
114
+
115
+ $reg->register('Sanitize', 'Blogger_Importer_Sanitize');
116
+ $feed->sanitize = $reg->create('Sanitize'); //Should not really need to do this but there seems to be an issue with the SimplePie class?
117
+ $reg->register('Item', 'WP_SimplePie_Blog_Item');
118
+
119
+ $feed->set_raw_data($response);
120
+ $feed->init();
121
+
122
+ foreach ($feed->get_items() as $item)
123
+ {
124
+ $blogentry = new BloggerEntry();
125
+
126
+ $blogentry->blogurl = $this->host;
127
+ $blogentry->id = $item->get_id();
128
+ $blogentry->published = $item->get_published();
129
+ $blogentry->updated = $item->get_updated();
130
+ $blogentry->isDraft = $item->get_draft_status();
131
+ $blogentry->title = $item->get_title();
132
+ $blogentry->content = $item->get_content();
133
+ $blogentry->author = $item->get_author()->get_name();
134
+ $blogentry->geotags = $item->get_geotags();
135
+
136
+ $blogentry->links = $item->get_links(array('replies', 'edit', 'self', 'alternate'));
137
+ $blogentry->parselinks();
138
+
139
+ $blogentry->categories = $item->get_categories();
140
+
141
+ // Checks for duplicates
142
+ $post_id = $blogentry->post_exists();
143
+
144
+ if ($post_id != 0)
145
+ {
146
+ $this->posts_skipped++;
147
+ } else
148
+ {
149
+ //Unique new post so import it
150
+ $post_id = $blogentry->import();
151
+ $this->posts_done++;
152
+ $this->save_vars();
153
+ }
154
+
155
+ //Ref: Not importing properly http://core.trac.wordpress.org/ticket/19096
156
+ //Simplified this section to count what is loaded rather than parsing the results again
157
+ $start_index++;
158
+ }
159
+
160
+ $this->posts_start_index = $start_index;
161
+
162
+ $this->save_vars();
163
+
164
+ } while ($this->total_posts > $start_index && $this->have_time());
165
+ }
166
+ return ($this->total_posts <= $start_index);
167
+ }
168
+
169
+ function import_comments($connector)
170
+ {
171
+ if (isset($this->comments_start_index))
172
+ $start_index = (int)$this->comments_start_index;
173
+ else
174
+ $start_index = 1;
175
+
176
+ if ($start_index > 0 && $this->total_comments > 0)
177
+ {
178
+
179
+ $this->mode = 'comments';
180
+ do
181
+ {
182
+ $index = $struct = $entries = array();
183
+
184
+ //So we can link up the comments as we go we need to load them in reverse order
185
+ //Reverse the start index as the GData Blogger feed can't be sorted
186
+ $batch = ((floor(($this->total_comments - $start_index) / Blogger_Import::MAX_RESULTS) * Blogger_Import::MAX_RESULTS) + 1);
187
+
188
+ $response = $connector->oauth_get($this->comments_url,array('max-results' => Blogger_Import::MAX_RESULTS, 'start-index' => $batch));
189
+ // parse the feed
190
+ $feed = new SimplePie();
191
+ //3/1/2012 Updated syntax for registering the item
192
+ $reg = $feed->get_registry();
193
+ $reg->register('Item', 'WP_SimplePie_Blog_Item');
194
+ // Use the standard "stricter" sanitize class for comments
195
+ $feed->set_raw_data($response);
196
+ $feed->init();
197
+
198
+ //Reverse the batch so we load the oldest comments first and hence can link up nested comments
199
+ $comments = array_reverse($feed->get_items());
200
+
201
+ if (!is_null($comments))
202
+ {
203
+ foreach ($comments as $item)
204
+ {
205
+ $commententry = new CommentEntry();
206
+
207
+ $commententry->id = $item->get_id();
208
+ $commententry->updated = $item->get_updated();
209
+ $commententry->content = $item->get_content();
210
+ $commententry->author = $item->get_author()->get_name();
211
+ $commententry->authoruri = $item->get_author()->get_link();
212
+ $commententry->authoremail = $item->get_author()->get_email();
213
+
214
+ $commententry->source = $item->get_source();
215
+ $parts = parse_url($commententry->source);
216
+ $commententry->old_post_permalink = $parts['path']; //Will be something like this '/feeds/417730729915399755/posts/default/8397846992898424746'
217
+
218
+ $commententry->post_ID = BloggerEntry::get_post_by_oldID($commententry->old_post_permalink);
219
+
220
+ //Get the links
221
+ $commententry->links = $item->get_links(array('edit', 'self', 'alternate', 'related'));
222
+ $commententry->parselinks();
223
+
224
+ // Nested comment?
225
+ if (isset($commententry->related))
226
+ {
227
+ $commententry->parentcommentid = CommentEntry::get_comment_by_oldID($commententry->related);
228
+ }
229
+
230
+ //Perhaps could log errors here?
231
+ if ($commententry->post_ID != 0)
232
+ {
233
+ // Checks for duplicates
234
+ if ($comment_id = $commententry->exists())
235
+ {
236
+ $this->comments_skipped++;
237
+ } else
238
+ {
239
+ $comment_id = $commententry->import();
240
+ $this->comments_done++;
241
+ }
242
+ } else
243
+ {
244
+ $this->comments_skipped++;
245
+ }
246
+ $this->save_vars();
247
+ $start_index++;
248
+ }
249
+ }
250
+
251
+ $this->comments_start_index = $start_index;
252
+ $this->save_vars();
253
+ } while ($this->total_comments > $start_index && $this->have_time());
254
+ }
255
+ return ($this->total_comments <= $start_index);
256
+ }
257
+
258
+ function save_authors()
259
+ {
260
+ global $wpdb;
261
+
262
+ //Change to take authors as a parameter
263
+ $authors = (array )$_POST['authors'];
264
+
265
+ //Todo: Handle the case where there are no more authors to remap
266
+
267
+ // Get an array of posts => authors
268
+ $post_ids = (array )$wpdb->get_col($wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'blogger_blog' AND meta_value = %s", $this->host));
269
+ $post_ids = join(',', $post_ids);
270
+ $results = (array )$wpdb->get_results("SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN ($post_ids)");
271
+ foreach ($results as $row)
272
+ $authors_posts[$row->post_id] = $row->meta_value;
273
+
274
+ foreach ($authors as $author => $user_id)
275
+ {
276
+ $user_id = (int)$user_id;
277
+
278
+ // Skip authors that haven't been changed
279
+ if ($user_id == $this->authors[$author][1])
280
+ continue;
281
+
282
+ // Get a list of the selected author's posts
283
+ $post_ids = (array )array_keys($authors_posts, $this->authors[$author][0]);
284
+ $post_ids = join(',', $post_ids);
285
+
286
+ $wpdb->query($wpdb->prepare("UPDATE $wpdb->posts SET post_author = %d WHERE id IN ($post_ids)", $user_id));
287
+ $this->authors[$author][1] = $user_id;
288
+
289
+ //Should we be tidying up the post meta data here, rather than in restart?
290
+ //$wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = 'blogger_author'");
291
+ }
292
+ return '';
293
+ }
294
+
295
+
296
+ /**
297
+ * Return count of imported posts from WordPress database
298
+ *
299
+ * @return int
300
+ */
301
+ function count_imported_posts()
302
+ {
303
+ global $wpdb;
304
+ $count = 0;
305
+ $meta_key = 'blogger_blog';
306
+ $sql = $wpdb->prepare("SELECT count(post_id) as cnt FROM $wpdb->postmeta m
307
+ inner join $wpdb->posts p on p.id = m.post_id and post_type = 'post'
308
+ where meta_key = '%s' and meta_value = '%s'", $meta_key, $this->host);
309
+
310
+ $result = $wpdb->get_results($sql);
311
+
312
+ if (!empty($result))
313
+ $count = intval($result[0]->cnt);
314
+
315
+ // unset to save memory
316
+ unset($results);
317
+
318
+ return $count;
319
+ }
320
+
321
+ /**
322
+ * Return count of imported images from WordPress database
323
+ *
324
+ * @param string $bloghost url
325
+ * @return int
326
+ */
327
+ function count_imported_images()
328
+ {
329
+ global $wpdb;
330
+ $count = 0;
331
+ $meta_key = 'blogger_blog';
332
+ $sql = $wpdb->prepare("SELECT count(post_id) as cnt FROM $wpdb->postmeta m
333
+ inner join $wpdb->posts p on p.id = m.post_id and post_type = 'attachment'
334
+ where meta_key = '%s' and meta_value = '%s'", $meta_key, $this->host);
335
+
336
+ $result = $wpdb->get_results($sql);
337
+
338
+ if (!empty($result))
339
+ $count = intval($result[0]->cnt);
340
+
341
+ // unset to save memory
342
+ unset($results);
343
+
344
+ return $count;
345
+ }
346
+
347
+ /**
348
+ * Return count of imported comments from WordPress database
349
+ *
350
+ * @param string $bloghost url
351
+ * @return int
352
+ */
353
+ function count_imported_comments()
354
+ {
355
+ global $wpdb;
356
+ $count = 0;
357
+ $meta_key = 'blogger_blog';
358
+ $sql = $wpdb->prepare("SELECT count(post_id) as cnt FROM $wpdb->postmeta m
359
+ inner join $wpdb->posts p on p.id = m.post_id and post_type = 'post'
360
+ inner join $wpdb->comments c on p.id = c.comment_post_ID
361
+ where meta_key = '%s' and meta_value = '%s'
362
+ and c.comment_type <> 'pingback'", $meta_key, $this->host);
363
+
364
+ $result = $wpdb->get_results($sql);
365
+
366
+ if (!empty($result))
367
+ $count = intval($result[0]->cnt);
368
+
369
+ // unset to save memory
370
+ unset($results);
371
+
372
+ return $count;
373
+ }
374
+
375
+ function process_links()
376
+ {
377
+ //Update all of the links in the blog
378
+ global $wpdb;
379
+
380
+ $postsprocessed = $this->links_progress;
381
+ if ($postsprocessed == 0)
382
+ {
383
+ $linksprocessed = 0;
384
+ } else
385
+ {
386
+ $linksprocessed = $this->links_done;
387
+ }
388
+ $batchsize = 20;
389
+
390
+ $oldurlsearch = $this->host;
391
+
392
+ if (substr($oldurlsearch, 0, 3) == 'www.')
393
+ {
394
+ $oldurlsearch = substr($oldurlsearch, 3, strlen($oldurlsearch - 3));
395
+ }
396
+
397
+ $oldurlsearch = str_replace('.', '\.', $oldurlsearch);
398
+
399
+ $blogspot = stripos($oldurlsearch, '\.blogspot\.');
400
+ if ($blogspot)
401
+ { //Blogspot addresses can be international e.g. myblog.blogspot.com, myblog.blogspot.com.au or myblog.blogspot.co.uk or myblog.blogspot.de both resolve to the same blog.
402
+ //See http://www.searchenginejournal.com/google-blogger-url-censorship/39724/
403
+ $oldurlsearch = substr($oldurlsearch, 0, $blogspot + 12) . '[\w\.]{2,6}';
404
+ }
405
+
406
+ $loadedposts = get_posts(array('meta_key' => 'blogger_blog', 'meta_value' => $this->host, 'posts_per_page' => $batchsize, 'offset' => $postsprocessed, 'post_status' => array('draft', 'publish','future')));
407
+
408
+ //Stop if nothing left
409
+ if (count($loadedposts) == 0){
410
+ return true;
411
+ }
412
+
413
+ foreach ($loadedposts as $importedpost) {
414
+ $importedcontent = $importedpost->post_content;
415
+
416
+ $regexp = '<a\s[^>]*href=([\"\'`])(https?:\/\/(?:www\.)*' . $oldurlsearch . '\/)([^\" >]*?)\1[^>]*>(.*)<\/a>';
417
+ if (preg_match_all("/$regexp/siU", $importedcontent, $matches, PREG_SET_ORDER))
418
+ {
419
+ foreach ($matches as $match)
420
+ {
421
+ $HostURL = substr($match[2], 0, strlen($match[2]) - 1); //e.g. http://minitemp.blogspot.co.uk
422
+ $PageURL = '/' . $match[3]; //e.g. '/2011/04/what-happens-if-blog-title-is-really.html'
423
+ $sql = $wpdb->prepare("SELECT post_id FROM $wpdb->postmeta m
424
+ inner join $wpdb->posts p on p.id = m.post_id and post_type = 'post'
425
+ where meta_key = '%s' and meta_value = '%s'", 'blogger_permalink', $PageURL);
426
+
427
+ $linkpostid = $wpdb->get_var($sql);
428
+
429
+ if ($linkpostid != 0) {
430
+ $NewURL = get_permalink($linkpostid);
431
+ } else { // Page not found, update content with just the new domain
432
+ $NewURL = site_url($PageURL);
433
+ }
434
+
435
+ $importedcontent = str_replace($HostURL . $PageURL, $NewURL, $importedcontent);
436
+ $linksprocessed++;
437
+ }
438
+
439
+ if ($importedcontent == '')
440
+ {
441
+ return new WP_Error('Empty Content', __("Attempting to write back empty content"));
442
+ }
443
+
444
+ $importedpost->post_content = $importedcontent;
445
+ wp_update_post($importedpost);
446
+
447
+ }
448
+ $postsprocessed++;
449
+
450
+ //For some reason the intermediate values are not getting written, is it that the options are cached hence not read back?
451
+ $this->links_done = $linksprocessed;
452
+ $this->links_progress = $postsprocessed;
453
+ $saved = $this->save_vars();
454
+ }
455
+ unset($loadedposts);
456
+ return ($postsprocessed >= $this->total_posts);;
457
+ }
458
+
459
+ function isurlimage($srcurl)
460
+ {
461
+ //Process picasaweb links and files that are images
462
+ if (substr($srcurl, 0, 27) == 'http://picasaweb.google.com')
463
+ return true;
464
+ return preg_match('/(?i)\.(jpe?g|png|gif|bmp)$/i', $srcurl);
465
+ }
466
+
467
+ //Get the current status for this blog
468
+ function get_js_status()
469
+ {
470
+ //Trap for division by zero errors
471
+ if ($this->total_posts == 0) {
472
+ $p1 = 100;
473
+ $i2 = 100;
474
+ $l2 = 100;
475
+ } else {
476
+ $p1 = ($this->posts_done / $this->total_posts) * 100;
477
+ $i2 = ($this->images_progress / $this->total_posts) * 100;
478
+ $l2 = ($this->links_progress / $this->total_posts) * 100;
479
+ }
480
+
481
+ $p2 = sprintf(__('%d of %d', 'blogger-importer'), $this->posts_done, $this->total_posts);
482
+ $p3 = sprintf(__('%d posts skipped', 'blogger-importer'), $this->posts_skipped);
483
+ $c1 = $this->total_comments == 0 ? 100 : ($this->comments_done / $this->total_comments) * 100;
484
+ $c2 = sprintf(__('%d of %d', 'blogger-importer'), $this->comments_done, $this->total_comments);
485
+ $c3 = sprintf(__('%d comments skipped', 'blogger-importer'), $this->comments_skipped);
486
+ $i1 = isset($this->images_done) ? (int)$this->images_done : 0;
487
+ $i3 = sprintf(__('%d images skipped', 'blogger-importer'), $this->images_skipped);
488
+
489
+ $l1 = isset($this->links_done) ? (int)$this->links_done : 0;
490
+
491
+ return "{p1:$p1,p2:\"$p2\",p3:\"$p3\",c1:$c1,c2:\"$c2\",c3:\"$c3\",i1:$i1,i2:$i2,i3:\"$i3\",l1:$l1,l2:$l2}";
492
+ }
493
+
494
+ function get_authors()
495
+ {
496
+ global $wpdb;
497
+ global $current_user;
498
+
499
+ $post_ids = (array )$wpdb->get_col($wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'blogger_blog' AND meta_value = %s", $this->host));
500
+ $post_ids = join(',', $post_ids);
501
+ $authors = (array )$wpdb->get_col($wpdb->prepare("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN (%s)",$post_ids));
502
+
503
+ $this->authors = array_map(null, $authors, array_fill(0, count($authors), $current_user->ID));
504
+ }
505
+
506
+ /*
507
+ * Search for either a linked image or a non linked image within the supplied html
508
+ * <a href="xxx" yyyy><img src="zzz" ></a> or <img src="zzz" >
509
+ * Ref: http://www.the-art-of-web.com/php/parse-links/
510
+ * "<a\s[^>]*href=(\"??)([^\" >]*?)\\1[^>]*>(.*)<\/a>"
511
+ * http://wordpress.org/extend/plugins/blogger-image-import/
512
+ * "<a[^>]+href\=([\"'`])(.*)\\1[^<]*?<img[^>]*src\=([\"'`])(.*)\\3[^>]*>"
513
+ */
514
+ function get_images($content)
515
+ {
516
+ $highrez = array();
517
+ $lowrez = array();
518
+
519
+ //First images with links
520
+ //Might be nice to expand this top try and get Alt and/or Title attributes for use as description
521
+ $regexp = "<a\s[^>]*href\=([\"'`])([^> ]*?)\\1[^<]*?<img[^>]*src\=([\"'`])([^\> ]*?)\\3[^>]*>";
522
+
523
+ if (preg_match_all("/$regexp/siU", $content, $matches1, PREG_SET_ORDER))
524
+ {
525
+ //http://www.techrepublic.com/article/17-useful-functions-for-manipulating-arrays-in-php/5792851
526
+ foreach ($matches1 as $match)
527
+ {
528
+ if ($this->isurlimage($match[2]))
529
+ {
530
+ $highrez[$match[4]] = $match[2];
531
+ } else
532
+ {
533
+ $lowrez[$match[4]] = '';
534
+ }
535
+ }
536
+ }
537
+
538
+ //Now any image (n.b. this overlaps the previous set)
539
+ $regexp = "<img[^>]*src\=([\"'`])([^\> ]*?)\\1[^>]*>";
540
+
541
+ if (preg_match_all("/$regexp/siU", $content, $matches2, PREG_SET_ORDER))
542
+ {
543
+ foreach ($matches2 as $match)
544
+ {
545
+ $lowrez[$match[2]] = '';
546
+ }
547
+ }
548
+
549
+ //Remove any rows from this second set that are already in the first set and merge two sets of results
550
+ $images = array_merge($lowrez, $highrez);
551
+ return $images;
552
+ }
553
+
554
+ /**
555
+ * Update all of the images in the posts that have already been imported
556
+ */
557
+ function process_images()
558
+ {
559
+ $postsprocessed = $this->images_progress;
560
+ if ($postsprocessed == 0)
561
+ {
562
+ $imagesprocessed = 0;
563
+ $imagesskipped = 0;
564
+ } else
565
+ {
566
+ $imagesprocessed = $this->images_done;
567
+ $imagesskipped = $this->images_skipped;
568
+ }
569
+
570
+ $batchsize = 20;
571
+
572
+ $loadedposts = get_posts(array('meta_key' => 'blogger_blog', 'meta_value' => $this->host, 'posts_per_page' => $batchsize, 'offset' => $postsprocessed, 'post_status' => array('draft', 'publish','future')));
573
+
574
+ //Stop if nothing left
575
+ if (count($loadedposts) == 0){
576
+ return true;
577
+ }
578
+
579
+ foreach ($loadedposts as $importedpost)
580
+ {
581
+
582
+ $importedcontent = $importedpost->post_content;
583
+ $author = get_post_meta($importedpost->ID, 'blogger_author', true);
584
+
585
+ $img_count = 0; //Count of images for this post
586
+ foreach ($this->get_images($importedcontent) as $lowrez => $highrez)
587
+ {
588
+ if (!$this->image_filter($lowrez))
589
+ {
590
+ //Pass null for description so that the default (filename) is used, might be good to use Alt tag instead?
591
+ $newcontent = $this->import_image($importedpost->ID, $lowrez, $highrez, null, $img_count, $importedcontent, $this->host, $author);
592
+ if (!is_wp_error($newcontent)) {
593
+ $importedcontent = $newcontent;
594
+ $img_count++;
595
+ } else {
596
+ Blogger_Import::_log($newcontent);
597
+ $imagesskipped++;
598
+ }
599
+ }
600
+ }
601
+ $imagesprocessed += $img_count;
602
+
603
+ $importedpost->post_content = $importedcontent;
604
+ wp_update_post($importedpost);
605
+ $postsprocessed++;
606
+
607
+ //For some reason the intermediate values are not getting written
608
+ $this->images_done = $imagesprocessed;
609
+ $this->images_progress = $postsprocessed;
610
+ $this->images_skipped = $imagesskipped;
611
+ $saved = $this->save_vars();
612
+ }
613
+ unset($loadedposts);
614
+ return ($postsprocessed >= $this->total_posts);
615
+ }
616
+
617
+ function image_urlremap($url,$large)
618
+ { /* Fix problem urls
619
+ e.g. change https://lh4.googleusercontent.com/-nt66qhxzDyY/TZOD-RhTYMI/AAAAAAAACd4/Elzm1smRFb4/s800-h/Ski%2520Trip.jpg to
620
+ to https://lh4.googleusercontent.com/-nt66qhxzDyY/TZOD-RhTYMI/AAAAAAAACd4/Elzm1smRFb4/s800/Ski%2520Trip.jpg
621
+ Could use a apply_filter here to allow users to add their own tweeks
622
+ */
623
+ $pattern = '/(\/)(s\d*)-h(\/)/i';
624
+ $replacement = '$1$2$3';
625
+ $img = preg_replace($pattern, $replacement, $url);
626
+
627
+ /* Strip out ? and # on the end of files */
628
+ $pattern = '/(.*)[#\?].*/i';
629
+ $replacement = '$1';
630
+ $img = preg_replace($pattern, $replacement, $img);
631
+
632
+ if ($large) {
633
+ // For images on blogger we can swap /sXXX/ with for example /s1600/ to get a larger file.
634
+ // Use a standardised large size so we can control quality vs filesize.
635
+ $pattern = '/(\/)(s\d*)(\/)/i';
636
+ $replacement = '$1s'.Blogger_Import::LARGE_IMAGE_SIZE.'$3';
637
+ $img = preg_replace($pattern, $replacement, $img);
638
+ }
639
+ return $img;
640
+ }
641
+
642
+ function image_filter($url)
643
+ {
644
+ // Do we exclude this particular image?
645
+ // Don't include images that are already loaded onto this site
646
+ // Could use a apply_filter here to allow users to add their own tweeks
647
+ return (substr($url, 0, strlen(site_url())) == site_url());
648
+ }
649
+
650
+ function import_image($post_id, $lowrez, $highrez, $description, $imgcount, $postcontent, $blogname, $author)
651
+ {
652
+ /* Import a new image unless we specifically filter it out or if it has already been downloaded on another page.
653
+ Based on http://wordpress.stackexchange.com/questions//media-sideload-image-file-name and the tumblr-importer
654
+ Simple filename cleaning as characters such as +, % cause issues ref: http://wordpress.org/extend/plugins/uploadplus/
655
+
656
+ It's processing links of a form similar to these as provided by the "get_images" function
657
+ <a href="myhighrezimage.jpg"><img src="mylowrezimage.jpg"></a>
658
+ or
659
+ <img src="mylowrezimage.jpg">
660
+
661
+ If the high resolution (linked) file is not an image then the low resolution version is downloaded.
662
+ */
663
+ $lowrez_old = $lowrez;
664
+ $highrez_old = $highrez;
665
+ $highrezispage = false;
666
+
667
+ $lowrez = $this->image_urlremap($lowrez,false);
668
+ if ($lowrez == '')
669
+ return new WP_Error('Not an image',$message = __('Lowrez not an image','blogger-importer'), $data = array($lowrez_old, $highrez_old));
670
+
671
+ if ($highrez != '')
672
+ {
673
+ $highrez = $this->image_urlremap($highrez,true);
674
+ } else
675
+ {
676
+ $highrez = $this->image_urlremap($lowrez,true);
677
+ }
678
+
679
+ if (!$att_id = $this->image_exists($lowrez))
680
+ {
681
+ //Option to add a timeout to download_url, but don't use the wp_remote_get timeout as that's much shorter than the default here of 300s
682
+ $tmp = download_url($highrez);
683
+
684
+ if (is_wp_error($tmp))
685
+ {
686
+ @unlink($tmp); // clean up, copied this from other examples but how is this supposed to work if $tmp is an error??
687
+ //Don't exit as can still try the small image
688
+ }
689
+
690
+ // If the highrez was not an image then try the lowrex
691
+ if (!$this->is_image($tmp, $highrez))
692
+ {
693
+ $highrezispage = true; //That image was not valid
694
+ $tmp = download_url($lowrez); // Option to add a timeout here
695
+
696
+ if (is_wp_error($tmp))
697
+ {
698
+ @unlink($tmp); // clean up
699
+ return $tmp; // output wp_error
700
+ }
701
+
702
+ if (!$this->is_image($tmp, $lowrez))
703
+ {
704
+ @unlink($tmp); // clean up None of items are actually images, for example might be a single pixel, deliberately filtered out or a 404 error?
705
+ return new WP_Error('No Images',__('None of the images are valid','blogger-importer'), $data = array($lowrez_old, $highrez_old));
706
+ }
707
+ }
708
+
709
+ $new_name = preg_replace('/[^A-Za-z0-9._ ]/i', '-', basename($lowrez));
710
+
711
+ $file_array = array('name' => $new_name, 'tmp_name' => $tmp);
712
+
713
+ $att_id = media_handle_sideload($file_array, $post_id, $description, array('post_excerpt' => $description));
714
+ if (is_wp_error($att_id))
715
+ {
716
+ @unlink($file_array['tmp_name']);
717
+ return $att_id;
718
+ }
719
+
720
+ // Link attachment upto old url, store the author so we can replace it later
721
+ add_post_meta($att_id, 'blogger_permalink', $lowrez);
722
+ add_post_meta($att_id, 'blogger_blog', $blogname, true);
723
+ add_post_meta($att_id, 'blogger_author', $author, true);
724
+
725
+ if ($highrezispage) //Not an image so store so we can link later
726
+ add_post_meta($att_id, 'blogger_largeimgispage', true);
727
+
728
+ } else
729
+ {
730
+ //Image already exists, check if the high rez one was valid last time
731
+ $tmp = get_post_meta($att_id, 'blogger_largeimgispage', true);
732
+ if ($tmp == true)
733
+ $highrezispage = true;
734
+ }
735
+
736
+ //Always treat picassa webs as image so they get replaced with the new High rez link
737
+ if (substr($highrez, 0, 27) == 'http://picasaweb.google.com')
738
+ $highrezispage = false;
739
+
740
+ //Replace the image strings
741
+ if (!$highrezispage && $highrez_old != '')
742
+ {
743
+ $highrez_new = reset(wp_get_attachment_image_src($att_id, 'full'));
744
+ $postcontent = str_replace($highrez_old, $highrez_new, $postcontent);
745
+ }
746
+ $lowrez_new = reset(wp_get_attachment_image_src($att_id, 'medium'));
747
+ $postcontent = str_replace($lowrez_old, $lowrez_new, $postcontent);
748
+
749
+ //Set the first image to be the post thumbnail (zero index)
750
+ if ($imgcount == 0)
751
+ {
752
+ set_post_thumbnail($post_id, $att_id);
753
+ }
754
+
755
+ //media handle sideload moves the file so there should be no temp file left but cleanup just incase.
756
+ @unlink($tmp);
757
+
758
+ // incase something goes wrong
759
+ if ($postcontent == '')
760
+ {
761
+ return new WP_Error('Empty Content', __("Attempting to write back empty content",'blogger-importer'), $data = array($lowrez_old, $highrez_old));
762
+ }
763
+ return $postcontent;
764
+ }
765
+
766
+ function is_image($file, $filename)
767
+ {
768
+ //Is the downloaded file really an image
769
+ //e.g. it looked like an image from the URL but when downloaded it was something else perhaps a html page
770
+ //Also filter out tracking images of 1 pixel square
771
+ //Found that wp_check_filetype_and_ext and wp_match_mime_types was giving false positives
772
+ $imgstats = @getimagesize($file);
773
+ if (!$imgstats)
774
+ {
775
+ return false;
776
+ }
777
+
778
+ return (($imgstats[0] > 1) && ($imgstats[1] > 1));
779
+ }
780
+
781
+ function image_exists($lowrez)
782
+ {
783
+ global $wpdb;
784
+ return $wpdb->get_var($wpdb->prepare("SELECT ID FROM $wpdb->posts p INNER JOIN $wpdb->postmeta m ON p.ID = m.post_id AND meta_key = 'blogger_permalink' WHERE post_type = 'attachment' AND meta_value = %s LIMIT 0 , 1",
785
+ $lowrez));
786
+ }
787
+
788
+ function have_time()
789
+ {
790
+ return (time() - $this->importer_started < Blogger_Import::MAX_EXECUTION_TIME);
791
+ }
792
+
793
+ function save_vars()
794
+ {
795
+ $vars = get_object_vars($this);
796
+ //http://core.trac.wordpress.org/ticket/13480
797
+ //Calling update options multiple times in a page (or ajax call) means that the cache kicks in and does not save to DB??
798
+
799
+ //Store each blog it it's own setting so that we retrieve and save less data each time it updates the stats
800
+ if (!update_option('blogger_importer_blog_'.$this->ID, $vars)) {
801
+ Blogger_Import::_log('Error saving blogger status');
802
+ Blogger_Import::_log(var_export(get_object_vars($this),true));
803
+ };
804
+
805
+ return !empty($vars);
806
+ }
807
+
808
+
809
+ }
810
+
811
+ ?>
blogger-importer-blogitem.php CHANGED
@@ -1,123 +1,163 @@
1
- <?php
2
-
3
- /**
4
- * Based on WP_SimplePieAtomPub_Item
5
- * Expect this to become part of core wordpress at some point.
6
- * See http://core.trac.wordpress.org/ticket/7652
7
- *
8
- * Todo GeoTag parsing
9
- * http://codex.wordpress.org/Geodata
10
- *
11
- */
12
-
13
- define('SIMPLEPIE_NAMESPACE_ATOMPUB', 'http://purl.org/atom/app#');
14
- define('SIMPLEPIE_NAMESPACE_GEOTAG', 'http://www.georss.org/georss');
15
-
16
- /**
17
- * SimplePie Helper for AtomPub
18
- *
19
- * @package WordPress
20
- * @subpackage Publishing
21
- * @since 3.1
22
- */
23
- if (!class_exists('WP_SimplePie_Blog_Item'))
24
- {
25
- class WP_SimplePie_Blog_Item extends SimplePie_Item
26
- {
27
- /**
28
- * Constructor
29
- */
30
- function WP_SimplePieAtomPub_Item($feed, $data)
31
- {
32
- parent::SimplePie_Item($feed, $data);
33
- }
34
-
35
- /**
36
- * Get the status of the entry
37
- *
38
- * @return bool True if the item is a draft, false otherwise
39
- */
40
- function get_draft_status()
41
- {
42
- $draft = false;
43
- if (($control = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOMPUB, 'control')) && !empty($control[0]['child'][SIMPLEPIE_NAMESPACE_ATOMPUB]['draft'][0]['data']))
44
- {
45
- $draft = ('yes' == $control[0]['child'][SIMPLEPIE_NAMESPACE_ATOMPUB]['draft'][0]['data']);
46
- }
47
- return $draft;
48
- }
49
-
50
- //Tried using date functions from http://core.trac.wordpress.org/attachment/ticket/7652/7652-separate.diff
51
- //but ended up with 1970s dates so returned to Otto's version which is much simplified
52
- function get_updated()
53
- {
54
- $temparray = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated');
55
- if (isset($temparray[0]['data']))
56
- return $this->convert_date($temparray[0]['data']);
57
- else
58
- return null;
59
- }
60
-
61
- function get_published()
62
- {
63
- $temparray = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published');
64
- if (isset($temparray[0]['data']))
65
- return $this->convert_date($temparray[0]['data']);
66
- else
67
- return null;
68
- }
69
-
70
- function get_geotags()
71
- {//Return an array of geo tags see http://codex.wordpress.org/Geodata
72
- //example source
73
- // <georss:featurename>R�dhuspladsen 3, 1550 Copenhagen, Denmark</georss:featurename>
74
- // <georss:point>55.6760968 12.5683371</georss:point>
75
-
76
-
77
- $latlong = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEOTAG, 'point');
78
-
79
- if (isset($latlong[0]['data'])) {
80
- preg_match('/([0-9.-]+).+?([0-9.-]+)/', $latlong[0]['data'], $matches);
81
- $lat=(float)$matches[1];
82
- $long=(float)$matches[2];
83
- }
84
-
85
- if (!isset($lat) |!isset($long)) {
86
- return null; //Without lat long we can't have a valid location
87
- }
88
-
89
- $address = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEOTAG, 'featurename');
90
- if (isset($address[0]['data']))
91
- $geo_address = $address[0]['data'];
92
- else
93
- $geo_address = null;
94
-
95
- $geo = array('geo_latitude' => $lat, 'geo_longitude' => $long, 'geo_address' => $geo_address );
96
-
97
- return $geo;
98
- }
99
-
100
- function convert_date($date)
101
- {
102
- 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);
103
- $offset = iso8601_timezone_to_offset($date_bits[7]);
104
- $timestamp = gmmktime($date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1]);
105
- $timestamp -= $offset; // Convert from Blogger local time to GMT
106
- $timestamp += get_option('gmt_offset') * 3600; // Convert from GMT to WP local time
107
- return gmdate('Y-m-d H:i:s', $timestamp);
108
- }
109
-
110
- //Don't Sanitize the ID, the default get_id was cleaning our IDs and that meant that nested comments did not work
111
- function get_id()
112
- {
113
- if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id'))
114
- {
115
- return $return[0]['data'];
116
- }
117
- }
118
-
119
- }
120
-
121
- }
122
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  ?>
1
+ <?php
2
+
3
+ /**
4
+ * Based on WP_SimplePieAtomPub_Item
5
+ * Expect this to become part of core wordpress at some point.
6
+ * See http://core.trac.wordpress.org/ticket/7652
7
+ *
8
+ * http://codex.wordpress.org/Geodata
9
+ *
10
+ */
11
+
12
+ define('SIMPLEPIE_NAMESPACE_ATOMPUB', 'http://purl.org/atom/app#');
13
+ define('SIMPLEPIE_NAMESPACE_GEOTAG', 'http://www.georss.org/georss');
14
+
15
+ /**
16
+ * SimplePie Helper for AtomPub
17
+ *
18
+ * @package WordPress
19
+ * @subpackage Publishing
20
+ * @since 3.1
21
+ */
22
+ if (!class_exists('WP_SimplePie_Blog_Item'))
23
+ {
24
+ class WP_SimplePie_Blog_Item extends SimplePie_Item
25
+ {
26
+ /**
27
+ * Constructor
28
+ */
29
+ function __construct($feed, $data)
30
+ {
31
+ parent::__construct($feed, $data);
32
+ }
33
+
34
+ /**
35
+ * Get the status of the entry
36
+ *
37
+ * @return bool True if the item is a draft, false otherwise
38
+ */
39
+ function get_draft_status()
40
+ {
41
+ $draft = false;
42
+ if (($control = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOMPUB, 'control')) && !empty($control[0]['child'][SIMPLEPIE_NAMESPACE_ATOMPUB]['draft'][0]['data']))
43
+ {
44
+ $draft = ('yes' == $control[0]['child'][SIMPLEPIE_NAMESPACE_ATOMPUB]['draft'][0]['data']);
45
+ }
46
+ return $draft;
47
+ }
48
+
49
+ //Tried using date functions from http://core.trac.wordpress.org/attachment/ticket/7652/7652-separate.diff
50
+ //but ended up with 1970s dates so returned to Otto's version which is much simplified
51
+ function get_updated()
52
+ {
53
+ $temparray = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'updated');
54
+ if (isset($temparray[0]['data']))
55
+ return $this->convert_date($temparray[0]['data']);
56
+ else
57
+ return null;
58
+ }
59
+
60
+ function get_published()
61
+ {
62
+ $temparray = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'published');
63
+ if (isset($temparray[0]['data']))
64
+ return $this->convert_date($temparray[0]['data']);
65
+ else
66
+ return null;
67
+ }
68
+
69
+ function get_geotags()
70
+ {//Return an array of geo tags see http://codex.wordpress.org/Geodata
71
+ //example source
72
+ // <georss:featurename>R�dhuspladsen 3, 1550 Copenhagen, Denmark</georss:featurename>
73
+ // <georss:point>55.6760968 12.5683371</georss:point>
74
+
75
+
76
+ $latlong = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEOTAG, 'point');
77
+
78
+ if (isset($latlong[0]['data'])) {
79
+ preg_match('/([0-9.-]+).+?([0-9.-]+)/', $latlong[0]['data'], $matches);
80
+ $lat=(float)$matches[1];
81
+ $long=(float)$matches[2];
82
+ }
83
+
84
+ if (!isset($lat) |!isset($long)) {
85
+ return null; //Without lat long we can't have a valid location
86
+ }
87
+
88
+ $address = $this->get_item_tags(SIMPLEPIE_NAMESPACE_GEOTAG, 'featurename');
89
+ if (isset($address[0]['data']))
90
+ $geo_address = $address[0]['data'];
91
+ else
92
+ $geo_address = null;
93
+
94
+ $geo = array('geo_latitude' => $lat, 'geo_longitude' => $long, 'geo_address' => $geo_address );
95
+
96
+ return $geo;
97
+ }
98
+
99
+ function convert_date($date)
100
+ {
101
+ 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);
102
+ $offset = iso8601_timezone_to_offset($date_bits[7]);
103
+ $timestamp = gmmktime($date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1]);
104
+ $timestamp -= $offset; // Convert from Blogger local time to GMT
105
+ $timestamp += get_option('gmt_offset') * 3600; // Convert from GMT to WP local time
106
+ return gmdate('Y-m-d H:i:s', $timestamp);
107
+ }
108
+
109
+ //Don't Sanitize the ID, the default get_id was cleaning our IDs and that meant that nested comments did not work
110
+ function get_id()
111
+ {
112
+ if ($return = $this->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10, 'id'))
113
+ {
114
+ return $return[0]['data'];
115
+ }
116
+ }
117
+
118
+ //Prefiltered links
119
+ function get_links($linktypes) {
120
+
121
+ $mylinks = array();
122
+ foreach ($linktypes as $type)
123
+ {
124
+ $links =parent::get_links($type);
125
+
126
+ if (!is_null($links)) {
127
+ foreach ($links as $link) {
128
+ $mylinks[] = array('rel' => $type, 'href' => $link);
129
+ }
130
+ }
131
+ }
132
+ return $mylinks;
133
+ }
134
+
135
+ //Preprocessed categories
136
+ function get_categories() {
137
+ $cats = parent::get_categories();
138
+ $mycats = array();
139
+
140
+ if (!is_null($cats)) {
141
+ foreach ($cats as $cat) {
142
+ $mycats[] = $cat->term;
143
+ }
144
+ }
145
+ return $mycats;
146
+ }
147
+
148
+ function get_source() {
149
+ $temp = $this->get_item_tags('http://purl.org/syndication/thread/1.0', 'in-reply-to');
150
+
151
+ foreach ($temp as $t) {
152
+ if (isset($t['attribs']['']['source'])) {
153
+ $source = $t['attribs']['']['source'];
154
+ }
155
+ }
156
+ return $source;
157
+ }
158
+
159
+ }
160
+
161
+ }
162
+
163
  ?>
blogger-importer-connector.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @author Andy Clark
5
+ * @copyright 2013
6
+ * Connector for Google Blogger
7
+ *
8
+ */
9
+
10
+ class Blogger_Importer_Connector
11
+ {
12
+ var $token;
13
+ var $secret;
14
+ var $endpoint;
15
+ var $oauth_token;
16
+ var $callback_url;
17
+
18
+ /*
19
+ Make the initial connection to blogger, authorise tokens etc.
20
+ */
21
+ function connect($url)
22
+ {
23
+ // Establish an Blogger_OAuth consumer
24
+ $base_url = admin_url();
25
+ $request_token_endpoint = 'https://www.google.com/accounts/OAuthGetRequestToken';
26
+ $authorize_endpoint = 'https://www.google.com/accounts/OAuthAuthorizeToken';
27
+
28
+ $test_consumer = new Blogger_OAuthConsumer('anonymous', 'anonymous', null); // anonymous is a google thing to allow non-registered apps to work
29
+
30
+ if (!Blogger_OAuthUtil::sslinstalled())
31
+ {
32
+ return new WP_Error('OpenSSL', __('OpenSSL is not installed check your PHP.INI or ask your server provider to enable the OpenSSL module.','blogger-importer'));
33
+ }
34
+
35
+ //prepare to get request token
36
+ //https://developers.google.com/accounts/docs/OAuth_ref#RequestToken
37
+ $sig_method = new Blogger_OAuthSignatureMethod_HMAC_SHA1();
38
+ $parsed = parse_url($request_token_endpoint);
39
+ $params = array('callback' => $base_url, 'scope' => 'http://www.blogger.com/feeds/', 'xoauth_displayname' => 'WordPress');
40
+
41
+ $req_req = Blogger_OAuthRequest::from_consumer_and_token($test_consumer, null, "GET", $request_token_endpoint, $params);
42
+ $req_req->sign_request($sig_method, $test_consumer, null);
43
+
44
+ // go get the request tokens from Google
45
+ $req_response = wp_remote_get($req_req->to_url(), array('sslverify' => false, 'timeout' => Blogger_Import::REMOTE_TIMEOUT));
46
+ if (is_wp_error($req_response))
47
+ {
48
+ return $req_response;
49
+ }
50
+ $req_token = wp_remote_retrieve_body($req_response);
51
+ if (is_wp_error($req_token))
52
+ {
53
+ return $req_token;
54
+ }
55
+
56
+ // parse the tokens
57
+ parse_str($req_token, $tokens);
58
+
59
+ $oauth_token = $tokens['oauth_token'];
60
+ $oauth_token_secret = $tokens['oauth_token_secret'];
61
+
62
+ //AGC:10/10/2012 Added error handling here based on http://wordpress.org/support/topic/plugin-blogger-importer-invalid-token/page/2?replies=31#post-3243710
63
+ if ($oauth_token == '')
64
+ {
65
+ return new WP_Error('Invalid Token', __('Invalid Token: Check server date, firewall settings and transports (curl, streams and fsockopen)','blogger-importer'));
66
+ } else
67
+ {
68
+ $this->callback_url = add_query_arg(array( 'token' => $oauth_token, 'secret' => $oauth_token_secret ),$url);
69
+ $this->endpoint = $authorize_endpoint;
70
+ $this->oauth_token = $oauth_token;
71
+ return true;
72
+ }
73
+ }
74
+
75
+ /*
76
+ *
77
+ */
78
+ function auth_form(){
79
+ //$next_url = admin_url('index.php?import=blogger&amp;noheader=true');
80
+ $auth = esc_attr__('Authorize', 'blogger-importer');
81
+
82
+ return(sprintf ("<form action='%s' method='get'>
83
+ <p class='submit' style='text-align:left;'>
84
+ <input type='submit' class='button' value='%s' />
85
+ <input type='hidden' name='oauth_token' value='%s' />
86
+ <input type='hidden' name='oauth_callback' value='%s' />
87
+ </p>
88
+ </form>
89
+ </div>\n",$this->endpoint,$auth,$this->oauth_token,$this->callback_url));
90
+ }
91
+
92
+ /**
93
+ * We have a authorized request token now, so upgrade it to an access token
94
+ *
95
+ * @link https://developers.google.com/gdata/articles/oauth#UsingAccessToken
96
+ */
97
+ function auth($token,$secret)
98
+ {
99
+ $oauth_access_token_endpoint = 'https://www.google.com/accounts/OAuthGetAccessToken';
100
+
101
+ // auth the token
102
+ $test_consumer = new Blogger_OAuthConsumer('anonymous', 'anonymous', null);
103
+ $auth_token = new Blogger_OAuthConsumer($token, $secret);
104
+ $access_token_req = new Blogger_OAuthRequest("GET", $oauth_access_token_endpoint);
105
+ $access_token_req = $access_token_req->from_consumer_and_token($test_consumer, $auth_token, "GET", $oauth_access_token_endpoint);
106
+
107
+ $access_token_req->sign_request(new Blogger_OAuthSignatureMethod_HMAC_SHA1(), $test_consumer, $auth_token);
108
+
109
+ // Should be doing validation here, what if wp_remote calls failed?
110
+ $after_access_request = wp_remote_retrieve_body(wp_remote_get($access_token_req->to_url(), array('sslverify' => false, 'timeout' => Blogger_Import::REMOTE_TIMEOUT)));
111
+
112
+ parse_str($after_access_request, $access_tokens);
113
+
114
+ $this->token = $access_tokens['oauth_token'];
115
+ $this->secret = $access_tokens['oauth_token_secret'];
116
+
117
+ $this->save_vars();
118
+ }
119
+
120
+
121
+ /**
122
+ * Get a URL using the oauth token for authentication (returns false on failure)
123
+ */
124
+ function oauth_get($url,$params = null)
125
+ {
126
+ $test_consumer = new Blogger_OAuthConsumer('anonymous', 'anonymous', null);
127
+ $goog = new Blogger_OAuthConsumer($this->token, $this->secret, null);
128
+ $request = new Blogger_OAuthRequest("GET", $url, $params);
129
+
130
+ //Ref: Not importing properly http://core.trac.wordpress.org/ticket/19096
131
+ $blog_req = $request->from_consumer_and_token($test_consumer, $goog, 'GET', $url, $params);
132
+
133
+ $blog_req->sign_request(new Blogger_OAuthSignatureMethod_HMAC_SHA1(), $test_consumer, $goog);
134
+
135
+ $data = wp_remote_get($blog_req->to_url(), array('sslverify' => false, 'timeout' => Blogger_Import::REMOTE_TIMEOUT));
136
+
137
+ if (wp_remote_retrieve_response_code($data) == 200)
138
+ {
139
+ $response = wp_remote_retrieve_body($data);
140
+ } else
141
+ {
142
+ $response = false;
143
+ }
144
+
145
+ return $response;
146
+ }
147
+
148
+
149
+ //Need to check, why start index 2? Surely 1 would be better
150
+ function get_total_results($url)
151
+ {
152
+ $response = $this->oauth_get($url,array('max-results' => 1, 'start-index' => 2));
153
+
154
+ $feed = new SimplePie();
155
+ $feed->set_raw_data($response);
156
+ $feed->init();
157
+ $results = $feed->get_channel_tags('http://a9.com/-/spec/opensearchrss/1.0/', 'totalResults');
158
+
159
+ $total_results = $results[0]['data'];
160
+ unset($feed);
161
+ return (int)$total_results;
162
+ }
163
+
164
+ //Have we authorised the connection
165
+ function isconnected()
166
+ {
167
+ return (isset($this->secret));
168
+ }
169
+
170
+ function reset()
171
+ {
172
+ unset($this->secret);
173
+ unset($this->token);
174
+ delete_option('blogger_importer_connector');
175
+ }
176
+
177
+ function save_vars()
178
+ {
179
+ if (!update_option('blogger_importer_connector', $this)) {
180
+ Blogger_Import::_log('Error saving connection');
181
+ Blogger_Import::_log(var_export(get_object_vars($this),true));
182
+ };
183
+ return !empty($vars);
184
+ }
185
+
186
+ //Return the connection
187
+ static function read_option()
188
+ {
189
+ $connection = get_option('blogger_importer_connector',new Blogger_Importer_Connector());
190
+ return $connection;
191
+ }
192
+
193
+ }
194
+
195
+ ?>
blogger-importer-sanitize.php CHANGED
@@ -1,152 +1,134 @@
1
- <?php
2
-
3
- /**
4
- * New class to sanitize trusted content from blogger import
5
- * Based on the SimplePie_Sanitize class by Ryan Parman and Geoffrey Sneddon
6
- *
7
- */
8
-
9
- class Blogger_Importer_Sanitize extends Simplepie_Sanitize
10
- {
11
- // Private vars
12
- var $base;
13
-
14
- // Options
15
- var $image_handler = '';
16
- var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'font', 'form', 'frame', 'frameset', 'html', 'input', 'marquee', 'meta', 'script', 'style');
17
- //Allow iframe (new style) and embed, param and object(old style) so that we get youtube videos transferred
18
- //Allow object and noscript for Amazon widgets
19
- var $encode_instead_of_strip = false;
20
- var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'imageanchor', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc');
21
- //Allow styles so we don't have to redo in Wordpress
22
- //Brett Morgan from Google has confirmed that imageanchor is a made up attribute that is just used in the blogger editor so we can remove that
23
- var $output_encoding = 'UTF-8';
24
- var $enable_cache = true;
25
- var $cache_location = './cache';
26
- var $cache_name_function = 'md5';
27
- var $cache_class = 'SimplePie_Cache';
28
- var $file_class = 'SimplePie_File';
29
- var $timeout = 10;
30
- var $useragent = '';
31
- var $force_fsockopen = false;
32
-
33
- var $replace_url_attributes = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite',
34
- 'q' => 'cite');
35
-
36
- function _normalize_tag($matches)
37
- {
38
- return '<' . strtolower($matches[1]);
39
- }
40
-
41
- function sanitize($data, $type, $base = '')
42
- {
43
- //Simplified function
44
- $data = trim($data);
45
-
46
- // Remappings
47
- $data = str_replace('<br>', '<br />', $data);
48
- $data = str_replace('<hr>', '<hr />', $data);
49
- //<span style="font-weight:bold;">Workshopshed:</span> > <b>Workshopshed:</b>
50
- $data = preg_replace('|(<span style="font-weight:bold;">)(?<!<span style="font-weight:bold;">).*(.*)(</span>)|', '<strong>$2</strong>', $data);
51
-
52
- //N.B. Don't strip comments as blogger uses <!--more--> which is the same as Wordpress
53
-
54
- //Now clean up
55
- foreach ($this->strip_htmltags as $tag)
56
- {
57
- $pcre = "/<($tag)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$tag" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>|(\/)?>)/siU';
58
- while (preg_match($pcre, $data))
59
- {
60
- $data = preg_replace_callback($pcre, array(&$this, 'do_strip_htmltags'), $data);
61
- }
62
- }
63
-
64
- foreach ($this->strip_attributes as $attrib)
65
- {
66
- $data = preg_replace('/(<[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*)' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . trim($attrib) . '(?:\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?' .
67
- SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>/', '\1\2\3>', $data);
68
- }
69
-
70
- // Replace relative URLs
71
- $this->base = $base;
72
- foreach ($this->replace_url_attributes as $element => $attributes)
73
- {
74
- $data = $this->replace_urls($data, $element, $attributes);
75
- }
76
-
77
- // If image handling (caching, etc.) is enabled, cache and rewrite all the image tags.
78
- if (isset($this->image_handler) && ((string )$this->image_handler) !== '' && $this->enable_cache)
79
- {
80
- $images = SimplePie_Misc::get_element('img', $data);
81
- foreach ($images as $img)
82
- {
83
- if (isset($img['attribs']['src']['data']))
84
- {
85
- $image_url = call_user_func($this->cache_name_function, $img['attribs']['src']['data']);
86
- $cache = call_user_func(array($this->cache_class, 'create'), $this->cache_location, $image_url, 'spi');
87
- if ($cache->load())
88
- {
89
- $img['attribs']['src']['data'] = $this->image_handler . $image_url;
90
- $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data);
91
- } else
92
- {
93
- $file = &new $this->file_class($img['attribs']['src']['data'], $this->timeout, 5, array('X-FORWARDED-FOR' => $_SERVER['REMOTE_ADDR']), $this->useragent, $this->force_fsockopen);
94
- $headers = $file->headers;
95
- if ($file->success && ($file->method & SIMPLEPIE_FILE_SOURCE_REMOTE === 0 || ($file->status_code === 200 || $file->status_code > 206 && $file->status_code < 300)))
96
- {
97
- if ($cache->save(array('headers' => $file->headers, 'body' => $file->body)))
98
- {
99
- $img['attribs']['src']['data'] = $this->image_handler . $image_url;
100
- $data = str_replace($img['full'], SimplePie_Misc::element_implode($img), $data);
101
- } else
102
- {
103
- trigger_error("$this->cache_location is not writeable", E_USER_WARNING);
104
- }
105
- }
106
- }
107
- }
108
- }
109
- }
110
-
111
- // Having (possibly) taken stuff out, there may now be whitespace at the beginning/end of the data
112
- $data = trim($data);
113
-
114
- // Normalise tags
115
- $data = preg_replace_callback('|<(/?[A-Z]+)|', array(&$this, '_normalize_tag'), $data);
116
-
117
- return $data;
118
- }
119
-
120
- function replace_urls($data, $tag, $attributes)
121
- {
122
- if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags))
123
- {
124
- $elements = SimplePie_Misc::get_element($tag, $data);
125
- foreach ($elements as $element)
126
- {
127
- if (is_array($attributes))
128
- {
129
- foreach ($attributes as $attribute)
130
- {
131
- if (isset($element['attribs'][$attribute]['data']))
132
- {
133
- $element['attribs'][$attribute]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attribute]['data'], $this->base);
134
- $new_element = SimplePie_Misc::element_implode($element);
135
- $data = str_replace($element['full'], $new_element, $data);
136
- $element['full'] = $new_element;
137
- }
138
- }
139
- } elseif (isset($element['attribs'][$attributes]['data']))
140
- {
141
- $element['attribs'][$attributes]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attributes]['data'], $this->base);
142
- $data = str_replace($element['full'], SimplePie_Misc::element_implode($element), $data);
143
- }
144
- }
145
- }
146
- return $data;
147
- }
148
-
149
-
150
- }
151
-
152
- ?>
1
+ <?php
2
+
3
+ /**
4
+ * New class to sanitize trusted content from blogger import
5
+ * Based on the SimplePie_Sanitize class by Ryan Parman and Geoffrey Sneddon
6
+ *
7
+ */
8
+
9
+ class Blogger_Importer_Sanitize extends SimplePie_Sanitize
10
+ {
11
+ // Private vars
12
+ var $base;
13
+
14
+ // Options
15
+ var $image_handler = '';
16
+ var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'font', 'form', 'frame', 'frameset', 'html', 'input', 'marquee', 'meta', 'script', 'style');
17
+ //Allow iframe (new style) and embed, param and object(old style) so that we get youtube videos transferred
18
+ //Allow object and noscript for Amazon widgets
19
+ var $encode_instead_of_strip = false;
20
+ var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'imageanchor', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc', 'trbidi');
21
+ //Allow styles so we don't have to redo in WordPress
22
+ //Brett Morgan from Google has confirmed that imageanchor is a made up attribute that is just used in the blogger editor so we can remove that
23
+ //trbidi another blogger proprietory attribute
24
+ var $output_encoding = 'UTF-8';
25
+ var $enable_cache = true;
26
+ var $cache_location = './cache';
27
+ var $cache_name_function = 'md5';
28
+ var $cache_class = 'SimplePie_Cache';
29
+ var $file_class = 'SimplePie_File';
30
+ var $timeout = 10;
31
+ var $useragent = '';
32
+ var $force_fsockopen = false;
33
+
34
+ var $replace_url_attributes = array('a' => 'href', 'area' => 'href', 'blockquote' => 'cite', 'del' => 'cite', 'form' => 'action', 'img' => array('longdesc', 'src'), 'input' => 'src', 'ins' => 'cite',
35
+ 'q' => 'cite');
36
+
37
+ public function __construct()
38
+ {
39
+ parent::__construct();
40
+ }
41
+
42
+ function _normalize_tag($matches)
43
+ {
44
+ return '<' . strtolower($matches[1]);
45
+ }
46
+
47
+ function sanitize($data, $type, $base = '')
48
+ {
49
+ //Simplified function
50
+ $data = trim($data);
51
+
52
+ // Normalise tags (string replacement is case sensitive)
53
+ $data = preg_replace_callback('|<(/?[A-Z]+)|', array(&$this, '_normalize_tag'), $data);
54
+
55
+ // Remappings
56
+ $data = str_replace('<br>', '<br />', $data);
57
+ $data = str_replace('<hr>', '<hr />', $data);
58
+ //<span style="font-weight:bold;">Workshopshed:</span> > <b>Workshopshed:</b>
59
+ $data = preg_replace('|(<span style="font-weight:bold;">)(?<!<span style="font-weight:bold;">).*(.*)(</span>)|', '<strong>$2</strong>', $data);
60
+
61
+ //N.B. Don't strip comments as blogger uses <!--more--> which is the same as WordPress
62
+ // Comments might also contain section targetting e.g. <!-- google_ad_section_start -->
63
+
64
+ //Now clean up
65
+ foreach ($this->strip_htmltags as $tag)
66
+ {
67
+ $pcre = "/<($tag)" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . "(>(.*)<\/$tag" . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>|(\/)?>)/siU';
68
+ while (preg_match($pcre, $data))
69
+ {
70
+ $data = preg_replace_callback($pcre, array(&$this, 'do_strip_htmltags'), $data);
71
+ }
72
+ }
73
+
74
+ foreach ($this->strip_attributes as $attrib)
75
+ {
76
+ $data = preg_replace('/(<[A-Za-z][^\x09\x0A\x0B\x0C\x0D\x20\x2F\x3E]*)' . SIMPLEPIE_PCRE_HTML_ATTRIBUTE . trim($attrib) . '(?:\s*=\s*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\x09\x0A\x0B\x0C\x0D\x20\x22\x27\x3E][^\x09\x0A\x0B\x0C\x0D\x20\x3E]*)?))?' .
77
+ SIMPLEPIE_PCRE_HTML_ATTRIBUTE . '>/', '\1\2\3>', $data);
78
+ }
79
+
80
+ // Replace relative URLs
81
+ $this->base = $base;
82
+
83
+ foreach ($this->replace_url_attributes as $element => $attributes)
84
+ {
85
+ $data = $this->replace_urls($data, $element, $attributes);
86
+ }
87
+
88
+ // Images are handled as a separate step as we need to download them
89
+
90
+ // Having (possibly) taken stuff out, there may now be whitespace at the beginning/end of the data
91
+ $data = trim($data);
92
+
93
+ return $data;
94
+ }
95
+
96
+ function replace_urls($data, $tag, $attributes)
97
+ {
98
+ //This seems to do nothing at all!?
99
+ if (!is_array($this->strip_htmltags) || !in_array($tag, $this->strip_htmltags))
100
+ {
101
+ $elements = SimplePie_Misc::get_element($tag, $data);
102
+ foreach ($elements as $element)
103
+ {
104
+ if (is_array($attributes))
105
+ {
106
+ foreach ($attributes as $attribute)
107
+ {
108
+ if (isset($element['attribs'][$attribute]['data']))
109
+ {
110
+ $element['attribs'][$attribute]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attribute]['data'], $this->base);
111
+ $new_element = SimplePie_Misc::element_implode($element);
112
+ $data = str_replace($element['full'], $new_element, $data);
113
+ $element['full'] = $new_element;
114
+ }
115
+ }
116
+ } elseif (isset($element['attribs'][$attributes]['data']))
117
+ {
118
+ $element['attribs'][$attributes]['data'] = SimplePie_Misc::absolutize_url($element['attribs'][$attributes]['data'], $this->base);
119
+ $data = str_replace($element['full'], SimplePie_Misc::element_implode($element), $data);
120
+ }
121
+ }
122
+ }
123
+ return $data;
124
+ }
125
+
126
+ //Latest SimplePie checks for this function
127
+ public function set_registry(SimplePie_Registry $registry)
128
+ {
129
+ parent::set_registry($registry);
130
+ }
131
+
132
+ }
133
+
134
+ ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
blogger-importer-table.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @author Andy Clark
5
+ * @copyright 2013
6
+ * Ref:http://wpengineer.com/2426/wp_list_table-a-step-by-step-guide/
7
+ * Class to render the blog table
8
+ */
9
+
10
+ if( ! class_exists( 'WP_List_Table' ) ) {
11
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
12
+ }
13
+
14
+ class Blogger_Import_List_Table extends WP_List_Table {
15
+
16
+ //Use unique names here so that WP does not style our comments as pers some other tables.
17
+ function get_columns(){
18
+ $columns = array(
19
+ 'BL_name' => __('Blog Name','blogger-importer'),
20
+ 'BL_url' => __('Blog URL','blogger-importer'),
21
+ 'BL_posts' => __('Posts','blogger-importer'),
22
+ 'BL_comments' => __('Comments','blogger-importer'),
23
+ 'BL_images' => __('Images','blogger-importer'),
24
+ 'BL_links' => __('Links','blogger-importer'),
25
+ 'BL_action' => __('Action Button','blogger-importer')
26
+ );
27
+
28
+ return $columns;
29
+ }
30
+
31
+ function prepare_items($blogs,$JSInit) {
32
+
33
+ $columns = $this->get_columns();
34
+ $hidden = array();
35
+ $sortable = array();
36
+ $this->_column_headers = array($columns, $hidden, $sortable);
37
+ $this->JSInit = $JSInit;
38
+
39
+ foreach ($blogs as $i => $blog) {
40
+ $row = array();
41
+ $row['ID'] = $i;
42
+ $row['BL_name'] = esc_js($blog->title);
43
+ $row['BL_url'] = $blog->host;
44
+ $row['BL_posts'] = array('posts_done'=> ($blog->posts_done) ? (int)$blog->posts_done : 0,'total_posts' => $blog->total_posts);
45
+ $row['BL_comments'] = array('comments_done'=> ($blog->comments_done) ? (int)$blog->comments_done : 0,'total_comments' => $blog->total_comments);
46
+ $row['BL_images'] = $blog->images_done ? (int)$blog->images_done : 0;
47
+ $row['BL_links'] = $blog->links_done ? (int)$blog->links_done : 0;
48
+ $row['BL_action'] = $blog->mode;
49
+ $this->items[] = $row;
50
+ }
51
+ }
52
+
53
+ //Column Rendering
54
+ function column_BL_posts( $item ) {
55
+ return sprintf('<div ID="Posts%s"><div ID="PostsLabel%s" class="ui-progressbar-caption">'.__('%d of %d', 'blogger-importer').'</div></div>',$item['ID'],$item['ID'],$item['BL_posts']['posts_done'],$item['BL_posts']['total_posts']);
56
+ }
57
+
58
+ function column_BL_comments( $item ) {
59
+ return sprintf('<div ID="Comments%s"><div ID="CommentsLabel%s" class="ui-progressbar-caption">'.__('%d of %d', 'blogger-importer').'</div></div>',$item['ID'],$item['ID'],$item['BL_comments']['comments_done'],$item['BL_comments']['total_comments']);
60
+ }
61
+
62
+ function column_BL_images( $item ) {
63
+ return sprintf('<div ID="Images%s"><div ID="ImagesLabel%s" class="ui-progressbar-caption">%d</div></div>',$item['ID'],$item['ID'],$item['BL_images']);
64
+ }
65
+
66
+ function column_BL_links( $item ) {
67
+ return sprintf('<div ID="Links%s"><div ID="LinksLabel%s" class="ui-progressbar-caption">%d</div></div>',$item['ID'],$item['ID'],$item['BL_links']);
68
+ }
69
+
70
+ function column_BL_action( $item ) {
71
+ $start = esc_js(__('Import', 'blogger-importer'));
72
+ $continue = esc_js(__('Continue', 'blogger-importer'));
73
+ $stop = esc_js(__('Importing...', 'blogger-importer'));
74
+ $authors = esc_js(__('Set Authors', 'blogger-importer'));
75
+
76
+ if ($item['BL_action'] == 'init')
77
+ $value = $start;
78
+ elseif ($item['BL_action'] == 'posts' || $item['BL_action'] == 'comments')
79
+ $value = $continue;
80
+ else
81
+ $value = $authors;
82
+ $value = esc_attr($value);
83
+
84
+ return sprintf("<input type='submit' class='button' id='submit%s' value='%s' /><input type='hidden' name='blog' value='%s' />",$item['ID'],$value,$item['ID']);
85
+ }
86
+
87
+ function column_default( $item, $column_name ) {
88
+ switch( $column_name ) {
89
+ case 'BL_name':
90
+ case 'BL_url':
91
+ case 'BL_comments':
92
+ case 'BL_images':
93
+ case 'BL_links':
94
+ case 'BL_action':
95
+ return $item[ $column_name ];
96
+ default:
97
+ return print_r( $item, true ) ; //Show the whole array for troubleshooting purposes
98
+ }
99
+ }
100
+
101
+ //Run Javascript at the bottom of the page to pass table data through to JQuery
102
+ function _js_vars() {
103
+ printf( "<!--blogger-importer-table _js_vars -->
104
+ <script type='text/javascript'>
105
+ /* <![CDATA[ */
106
+ var blogs = {};
107
+ jQuery(function() {
108
+ %s
109
+ jQuery.each(blogs, function(i, me){me.init();});
110
+ jQuery('#Refresh').bind('click', function(){return jQuery.each(blogs, function(i, me){me.stop();})});
111
+ });
112
+ /* ]]> */
113
+ </script>\n",$this->JSInit );
114
+ }
115
+
116
+
117
+ }
118
+ ?>
blogger-importer.css CHANGED
@@ -1,29 +1,62 @@
1
-
2
- td { text-align: center; line-height: 2em;}
3
- thead td { font-weight: bold; }
4
- .bar {
5
- width: 200px;
6
- text-align: left;
7
- line-height: 2em;
8
- padding: 0px;
9
- }
10
- .ind {
11
- position: absolute;
12
- background-color: #83B4D8;
13
- width: 1px;
14
- z-index: 9;
15
- }
16
- .stat {
17
- z-index: 10;
18
- position: relative;
19
- text-align: center;
20
- }
21
- td.submit {
22
- margin:0;
23
- padding:0;
24
- }
25
-
26
- td {
27
- padding-left:10px;
28
- padding-right:10px;
29
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ td { text-align: center; line-height: 2em;}
3
+ thead td { font-weight: bold; }
4
+ .bar {
5
+ width: 200px;
6
+ text-align: left;
7
+ line-height: 2em;
8
+ padding: 0px;
9
+ }
10
+ .ind {
11
+ position: absolute;
12
+ background-color: #83B4D8;
13
+ width: 1px;
14
+ z-index: 9;
15
+ }
16
+ /* Todo: Review some of these since moving to WP_Tables */
17
+ .stat {
18
+ z-index: 10;
19
+ position: relative;
20
+ text-align: center;
21
+ }
22
+ td.submit {
23
+ margin:0;
24
+ padding:0;
25
+ }
26
+
27
+ td {
28
+ padding-left:10px;
29
+ padding-right:10px;
30
+ }
31
+
32
+ /* Fix for MP6 plugin aka future styling for Admin UI */
33
+ table.wp-list-table td { line-height: 2em;}
34
+
35
+ .column-BL_name { text-align: left;}
36
+ .column-BL_url { text-align: left;}
37
+ .column-BL_posts { text-align: left; width:12%; }
38
+ .column-BL_comments { text-align: left; width:12%;}
39
+ .column-BL_images { text-align: left; width:12%;}
40
+ .column-BL_links { text-align: left; width:12%;}
41
+ .column-BL_action { text-align: left; width:8%;}
42
+
43
+ td.column-BL_posts div { width: 100%; }
44
+
45
+ /* JQuery Progress Bars */
46
+ .ui-progressbar-value {background-color: #83B4D8;
47
+ position:relative;
48
+ height:2em;
49
+ margin-top:-2em;
50
+ z-index: 9;
51
+ display: block;}
52
+ .ui-progressbar {border: thin solid;
53
+ height:2em;
54
+ }
55
+
56
+ .ui-progressbar-caption {
57
+ z-index:10;
58
+ display: block;
59
+ position: relative;
60
+ text-align: center;
61
+
62
+ }
blogger-importer.js ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Javascript for refreshing the blog table. */
2
+ /* Localised strings passed to this via wp_localize_script and BL_strings object */
3
+ /* Stop on error or stop if Ajax Admin can't find a suitable handler */
4
+ function blog(i, title, mode, status){
5
+ this.blog = i;
6
+ this.mode = mode;
7
+ this.title = title;
8
+ eval('this.status='+status);
9
+ this.button = '#submit'+this.blog;
10
+ };
11
+ blog.prototype = {
12
+ start: function() {
13
+ this.cont = true;
14
+ this.kick();
15
+ this.check();
16
+ },
17
+ kick: function() {
18
+ ++this.kicks;
19
+ with ({currentblog: this.blog}) {
20
+ jQuery.post(BL_strings.ajaxURL,{
21
+ action: 'BL_import',
22
+ blogID:this.blog
23
+ },
24
+ function(text,result){blogs[currentblog].kickd(text,result)});
25
+ }
26
+ },
27
+ check: function() {
28
+ ++this.checks;
29
+ with ({currentblog: this.blog}) {
30
+ jQuery.post(BL_strings.ajaxURL,{
31
+ action: 'BL_status',
32
+ blogID:this.blog
33
+ },
34
+ function(text,result){blogs[currentblog].checkd(text,result)});
35
+ }
36
+ },
37
+ kickd: function(text, result) {
38
+ --this.kicks;
39
+ if ( result == 'error' || text == '0' ) {
40
+ // TODO: exception handling
41
+ this.stop();
42
+ } else {
43
+ if ( text == 'done' ) {
44
+ this.stop();
45
+ this.done();
46
+ this.check();
47
+ } else if ( text == 'nothing' ) {
48
+ this.stop();
49
+ this.nothing();
50
+ this.done();
51
+ } else if ( text == 'continue' ) {
52
+ this.kick();
53
+ } else if ( this.mode == 'stopped' )
54
+ jQuery(this.button).attr('value', BL_strings.cont);
55
+
56
+ }
57
+ },
58
+ checkd: function(text, result) {
59
+ --this.checks;
60
+ if ( result == 'error' || text == '0' ) {
61
+ // TODO: exception handling
62
+ this.stop();
63
+ } else {
64
+ eval('this.status='+text);
65
+ this.update();
66
+ if ( this.cont || this.kicks > 0 ) {
67
+ with ({currentblog: this.blog}) {
68
+ setTimeout(function() {
69
+ blogs[currentblog].check();
70
+ },parseInt(BL_strings.interval));
71
+ }
72
+ }
73
+ }
74
+ },
75
+ update: function() {
76
+ try
77
+ {
78
+ jQuery('#Posts'+this.blog).progressbar({ value: this.status.p1});
79
+ jQuery('#PostsLabel'+this.blog).text(this.status.p2);
80
+ jQuery('#Posts'+this.blog).attr('title', this.status.p3);
81
+ jQuery('#Comments'+this.blog).progressbar({ value: this.status.c1 });
82
+ jQuery('#CommentsLabel'+this.blog).text(this.status.c2);
83
+ jQuery('#Comments'+this.blog).attr('title',this.status.c3);
84
+ jQuery('#Images'+this.blog).progressbar({ value: this.status.i2});
85
+ jQuery('#ImagesLabel'+this.blog).text(this.status.i1);
86
+ jQuery('#Images'+this.blog).attr('title',this.status.i3);
87
+ jQuery('#Links'+this.blog).progressbar({ value: this.status.l2 });
88
+ jQuery('#LinksLabel'+this.blog).text(this.status.l1);
89
+ }
90
+ catch(err)
91
+ {
92
+ txt="Error displaying updates for "+this.title+", importer cannot continue\n\n";
93
+ txt+="Error description: " + err.message + "\n\n";
94
+ txt+="Click OK to continue.\n\n";
95
+ alert(txt);
96
+ }
97
+ },
98
+ stop: function() {
99
+ this.cont = false;
100
+ },
101
+ done: function() {
102
+ this.mode = 'authors';
103
+ jQuery(this.button).attr('value', BL_strings.authors);
104
+ },
105
+ nothing: function() {
106
+ this.mode = 'nothing';
107
+ jQuery(this.button).remove();
108
+ alert(BL_strings.nothing);
109
+ },
110
+ getauthors: function() {
111
+ if ( jQuery('div.wrap').length > 1 )
112
+ jQuery('div.wrap').gt(0).remove();
113
+ jQuery('div.wrap').empty().append('<h2>'+BL_strings.authhead+'</h2><h3>' + this.title + '</h3>');
114
+ jQuery('div.wrap').append('<p id=\"auth\">'+BL_strings.loadauth+'</p>');
115
+ jQuery('p#auth').load('index.php?import=blogger&noheader=true&authors=1',{blog:this.blog});
116
+ },
117
+ init: function() {
118
+ this.update();
119
+ var i = this.blog;
120
+ jQuery(this.button).bind('click', function(){return blogs[i].click();});
121
+ this.kicks = 0;
122
+ this.checks = 0;
123
+ },
124
+ click: function() {
125
+ if ( this.mode == 'init' || this.mode == 'stopped' || this.mode == 'posts' || this.mode == 'comments' ) {
126
+ this.mode = 'started';
127
+ this.start();
128
+ jQuery(this.button).attr('value', BL_strings.stop);
129
+ } else if ( this.mode == 'started' ) {
130
+ //return false; // let it run...
131
+ this.mode = 'stopped';
132
+ this.stop();
133
+ if ( this.checks > 0 || this.kicks > 0 ) {
134
+ this.mode = 'stopping';
135
+ jQuery(this.button).attr('value', BL_strings.stopping);
136
+ } else {
137
+ jQuery(this.button).attr('value', BL_strings.cont);
138
+ }
139
+ } else if ( this.mode == 'authors' ) {
140
+ document.location = 'index.php?import=blogger&authors=1&blog='+this.blog;
141
+ //this.mode = 'authors2';
142
+ //this.getauthors();
143
+ }
144
+ return false;
145
+ }
146
+ };
blogger-importer.php CHANGED
@@ -1,1034 +1,502 @@
1
- <?php
2
- /*
3
- Plugin Name: Blogger Importer
4
- Plugin URI: http://wordpress.org/extend/plugins/blogger-importer/
5
- Description: Import posts, comments, and tags from a Blogger blog and migrate authors to Wordpress users.
6
- Author: wordpressdotorg
7
- Author URI: http://wordpress.org/
8
- Version: 0.6
9
- License: GPLv2
10
- License URI: 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
- // Load Simple Pie
20
- require_once ABSPATH . WPINC . '/class-feed.php';
21
- require_once 'blogger-importer-sanitize.php';
22
- require_once 'blogger-importer-blogitem.php';
23
-
24
- // Load OAuth library
25
- require_once 'oauth.php';
26
-
27
- if (!class_exists('WP_Importer'))
28
- {
29
- $class_wp_importer = ABSPATH . 'wp-admin/includes/class-wp-importer.php';
30
- if (file_exists($class_wp_importer))
31
- require_once $class_wp_importer;
32
- }
33
-
34
- /**
35
- * How many records per GData query
36
- *
37
- * @package WordPress
38
- * @subpackage Blogger_Import
39
- * @var int
40
- * @since unknown
41
- */
42
- define('MAX_RESULTS', 25);
43
-
44
- /**
45
- * How many seconds to let the script run
46
- *
47
- * @package WordPress
48
- * @subpackage Blogger_Import
49
- * @var int
50
- * @since unknown
51
- */
52
- define('MAX_EXECUTION_TIME', 20);
53
-
54
- /**
55
- * How many seconds between status bar updates
56
- *
57
- * @package WordPress
58
- * @subpackage Blogger_Import
59
- * @var int
60
- * @since unknown
61
- */
62
- define('STATUS_INTERVAL', 3);
63
-
64
- /**
65
- * Blogger Importer
66
- *
67
- * @package WordPress
68
- * @subpackage Importer
69
- */
70
- if (class_exists('WP_Importer'))
71
- {
72
- class Blogger_Import extends WP_Importer
73
- {
74
-
75
- function Blogger_Import()
76
- {
77
- global $importer_started;
78
- $importer_started = time();
79
- if (isset($_GET['import']) && $_GET['import'] == 'blogger')
80
- {
81
- add_action('admin_print_scripts', array(&$this, 'queue_scripts'));
82
- add_action('admin_print_styles', array(&$this, 'queue_style'));
83
- }
84
- }
85
-
86
- function queue_scripts($hook)
87
- {
88
- wp_enqueue_script('jquery');
89
- }
90
-
91
- function queue_style()
92
- {
93
- wp_enqueue_style('BloggerImporter', plugins_url('/blogger-importer.css', __file__));
94
- }
95
-
96
- // Shows the welcome screen and the magic auth link.
97
- function greet()
98
- {
99
- $next_url = get_option('siteurl') . '/wp-admin/index.php?import=blogger&amp;noheader=true';
100
- $auth_url = $this->get_oauth_link();
101
- $title = __('Import Blogger', 'blogger-importer');
102
- $welcome = __('Howdy! This importer allows you to import posts and comments from your Blogger account into your WordPress site.', 'blogger-importer');
103
- $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');
104
- $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');
105
- $auth = esc_attr__('Authorize', 'blogger-importer');
106
-
107
- echo "
108
- <div class='wrap'>
109
- " . screen_icon() . "
110
- <h2>$title</h2>
111
- <p>$welcome</p><p>$prereqs</p><p>$stepone</p>
112
- <form action='{$auth_url['url']}' method='get'>
113
- <p class='submit' style='text-align:left;'>
114
- <input type='submit' class='button' value='$auth' />
115
- <input type='hidden' name='oauth_token' value='{$auth_url['oauth_token']}' />
116
- <input type='hidden' name='oauth_callback' value='{$auth_url['oauth_callback']}' />
117
- </p>
118
- </form>
119
- </div>\n";
120
- }
121
-
122
- function get_oauth_link()
123
- {
124
- // Establish an Blogger_OAuth consumer
125
- $base_url = get_option('siteurl') . '/wp-admin';
126
- $request_token_endpoint = 'https://www.google.com/accounts/OAuthGetRequestToken';
127
- $authorize_endpoint = 'https://www.google.com/accounts/OAuthAuthorizeToken';
128
-
129
- $test_consumer = new Blogger_OAuthConsumer('anonymous', 'anonymous', null); // anonymous is a google thing to allow non-registered apps to work
130
-
131
- //prepare to get request token
132
- $sig_method = new Blogger_OAuthSignatureMethod_HMAC_SHA1();
133
- $parsed = parse_url($request_token_endpoint);
134
- $params = array('callback' => $base_url, 'scope' => 'http://www.blogger.com/feeds/', 'xoauth_displayname' => 'WordPress');
135
-
136
- $req_req = Blogger_OAuthRequest::from_consumer_and_token($test_consumer, null, "GET", $request_token_endpoint, $params);
137
- $req_req->sign_request($sig_method, $test_consumer, null);
138
-
139
- // go get the request tokens from Google
140
- $req_token = wp_remote_retrieve_body(wp_remote_get($req_req->to_url(), array('sslverify' => false)));
141
-
142
- // parse the tokens
143
- parse_str($req_token, $tokens);
144
-
145
- $oauth_token = $tokens['oauth_token'];
146
- $oauth_token_secret = $tokens['oauth_token_secret'];
147
-
148
- $callback_url = "$base_url/index.php?import=blogger&noheader=true&token=$oauth_token&token_secret=$oauth_token_secret";
149
-
150
- return array('url' => $authorize_endpoint, 'oauth_token' => $oauth_token, 'oauth_callback' => $callback_url);
151
- }
152
-
153
- function uh_oh($title, $message, $info)
154
- {
155
- echo "<div class='wrap'>";
156
- screen_icon();
157
- echo "<h2>$title</h2><p>$message</p><pre>$info</pre></div>";
158
- }
159
-
160
- function auth()
161
- {
162
- // we have a authorized request token now, so upgrade it to an access token
163
- $token = $_GET['token'];
164
- $token_secret = $_GET['token_secret'];
165
-
166
- $oauth_access_token_endpoint = 'https://www.google.com/accounts/OAuthGetAccessToken';
167
-
168
- // auth the token
169
- $test_consumer = new Blogger_OAuthConsumer('anonymous', 'anonymous', null);
170
- $auth_token = new Blogger_OAuthConsumer($token, $token_secret);
171
- $access_token_req = new Blogger_OAuthRequest("GET", $oauth_access_token_endpoint);
172
- $access_token_req = $access_token_req->from_consumer_and_token($test_consumer, $auth_token, "GET", $oauth_access_token_endpoint);
173
-
174
- $access_token_req->sign_request(new Blogger_OAuthSignatureMethod_HMAC_SHA1(), $test_consumer, $auth_token);
175
-
176
- $after_access_request = wp_remote_retrieve_body(wp_remote_get($access_token_req->to_url(), array('sslverify' => false)));
177
-
178
- parse_str($after_access_request, $access_tokens);
179
-
180
- $this->token = $access_tokens['oauth_token'];
181
- $this->token_secret = $access_tokens['oauth_token_secret'];
182
-
183
- wp_redirect(remove_query_arg(array('token', 'noheader')));
184
- }
185
-
186
- // get a URL using the oauth token for authentication (returns false on failure)
187
- function oauth_get($url, $params = null)
188
- {
189
- $test_consumer = new Blogger_OAuthConsumer('anonymous', 'anonymous', null);
190
- $goog = new Blogger_OAuthConsumer($this->token, $this->token_secret, null);
191
- $request = new Blogger_OAuthRequest("GET", $url, $params);
192
-
193
- //Ref: Not importing properly http://core.trac.wordpress.org/ticket/19096
194
- $blog_req = $request->from_consumer_and_token($test_consumer, $goog, 'GET', $url, $params);
195
-
196
- $blog_req->sign_request(new Blogger_OAuthSignatureMethod_HMAC_SHA1(), $test_consumer, $goog);
197
-
198
- $data = wp_remote_get($blog_req->to_url(), array('sslverify' => false));
199
-
200
- if (wp_remote_retrieve_response_code($data) == 200)
201
- {
202
- $response = wp_remote_retrieve_body($data);
203
- } else
204
- {
205
- $response == false;
206
- }
207
-
208
- return $response;
209
- }
210
-
211
- function show_blogs($iter = 0)
212
- {
213
- if (empty($this->blogs))
214
- {
215
- $xml = $this->oauth_get('https://www.blogger.com/feeds/default/blogs');
216
-
217
- // Give it a few retries... this step often flakes out the first time.
218
- if (empty($xml))
219
- {
220
- if ($iter < 3)
221
- {
222
- return $this->show_blogs($iter + 1);
223
- } else
224
- {
225
- $this->uh_oh(__('Trouble signing in', 'blogger-importer'), __('We were not able to gain access to your account. Try starting over.', 'blogger-importer'), '');
226
- return false;
227
- }
228
- }
229
-
230
- $feed = new SimplePie();
231
- $feed->set_raw_data($xml);
232
- $feed->init();
233
-
234
- foreach ($feed->get_items() as $item)
235
- {
236
- $blog = array(); //reset
237
- $blog['title'] = $item->get_title();
238
- $blog['summary'] = $item->get_description();
239
-
240
- //ID is of the form tag:blogger.com,1999:blog-417730729915399755
241
- //We need that number from the end
242
- $rawid = explode('-', $item->get_id());
243
- $blog['id'] = $rawid[count($rawid) - 1];
244
-
245
- $parts = parse_url($item->get_link(0, 'alternate'));
246
- $blog['host'] = $parts['host'];
247
- $blog['gateway'] = $item->get_link(0, 'edit');
248
- $blog['posts_url'] = $item->get_link(0, 'http://schemas.google.com/g/2005#post');
249
-
250
- //AGC:20/4/2012 Developers guide suggests that the correct feed is located as follows
251
- //See https://developers.google.com/blogger/docs/1.0/developers_guide_php
252
- $blog['comments_url'] = "http://www.blogger.com/feeds/{$blog['id']}/comments/default";
253
-
254
- if (!empty($blog))
255
- {
256
- $blog['total_posts'] = $this->get_total_results($blog['posts_url']);
257
- $blog['total_comments'] = $this->get_total_results($blog['comments_url']);
258
-
259
- $blog['mode'] = 'init';
260
- $this->blogs[] = $blog;
261
- }
262
-
263
- }
264
-
265
- if (empty($this->blogs))
266
- {
267
- $this->uh_oh(__('No blogs found', 'blogger-importer'), __('We were able to log in but there were no blogs. Try a different account next time.', 'blogger-importer'), '');
268
- return false;
269
- }
270
- }
271
-
272
- //Should probably be using WP_LIST_TABLE here rather than manually rendering a table in html
273
- //http://wpengineer.com/2426/wp_list_table-a-step-by-step-guide/
274
- //echo '<pre>'.print_r($this,1).'</pre>';
275
- $start = esc_js(__('Import', 'blogger-importer'));
276
- $continue = esc_js(__('Continue', 'blogger-importer'));
277
- $stop = esc_js(__('Importing...', 'blogger-importer'));
278
- $authors = esc_js(__('Set Authors', 'blogger-importer'));
279
- $loadauth = esc_js(__('Preparing author mapping form...', 'blogger-importer'));
280
- $authhead = esc_js(__('Final Step: Author Mapping', 'blogger-importer'));
281
- $nothing = esc_js(__('Nothing was imported. Had you already imported this blog?', 'blogger-importer'));
282
- $stopping = ''; //Missing String used below.
283
- $title = __('Blogger Blogs', 'blogger-importer');
284
- $name = __('Blog Name', 'blogger-importer');
285
- $url = __('Blog URL', 'blogger-importer');
286
- $action = __('The Magic Button', 'blogger-importer');
287
- $posts = __('Posts', 'blogger-importer');
288
- $comments = __('Comments', 'blogger-importer');
289
- $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.',
290
- 'blogger-importer');
291
-
292
- $interval = STATUS_INTERVAL * 1000;
293
- $init = '';
294
- $rows = '';
295
-
296
- foreach ($this->blogs as $i => $blog)
297
- {
298
- if ($blog['mode'] == 'init')
299
- $value = $start;
300
- elseif ($blog['mode'] == 'posts' || $blog['mode'] == 'comments')
301
- $value = $continue;
302
- else
303
- $value = $authors;
304
- $value = esc_attr($value);
305
- $blogtitle = esc_js($blog['title']);
306
- $pdone = isset($blog['posts_done']) ? (int)$blog['posts_done'] : 0;
307
- $cdone = isset($blog['comments_done']) ? (int)$blog['comments_done'] : 0;
308
- $init .= "blogs[$i]=new blog($i,'$blogtitle','{$blog['mode']}','" . $this->get_js_status($i) . '\');';
309
- $pstat = "<div class='ind' id='pind$i'>&nbsp;</div><div id='pstat$i' class='stat'>$pdone/{$blog['total_posts']}</div>";
310
- $cstat = "<div class='ind' id='cind$i'>&nbsp;</div><div id='cstat$i' class='stat'>$cdone/{$blog['total_comments']}</div>";
311
- $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";
312
- }
313
-
314
- echo "<div class='wrap'>". screen_icon() ."<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>";
315
- echo "
316
- <script type='text/javascript'>
317
- /* <![CDATA[ */
318
- var strings = {cont:'$continue',stop:'$stop',stopping:'$stopping',authors:'$authors',nothing:'$nothing'};
319
- var blogs = {};
320
- function blog(i, title, mode, status){
321
- this.blog = i;
322
- this.mode = mode;
323
- this.title = title;
324
- eval('this.status='+status);
325
- this.button = document.getElementById('submit'+this.blog);
326
- };
327
- blog.prototype = {
328
- start: function() {
329
- this.cont = true;
330
- this.kick();
331
- this.check();
332
- },
333
- kick: function() {
334
- ++this.kicks;
335
- var i = this.blog;
336
- jQuery.post('admin.php?import=blogger&noheader=true',{blog:this.blog},function(text,result){blogs[i].kickd(text,result)});
337
- },
338
- check: function() {
339
- ++this.checks;
340
- var i = this.blog;
341
- jQuery.post('admin.php?import=blogger&noheader=true&status=true',{blog:this.blog},function(text,result){blogs[i].checkd(text,result)});
342
- },
343
- kickd: function(text, result) {
344
- if ( result == 'error' ) {
345
- // TODO: exception handling
346
- if ( this.cont )
347
- setTimeout('blogs['+this.blog+'].kick()', 1000);
348
- } else {
349
- if ( text == 'done' ) {
350
- this.stop();
351
- this.done();
352
- } else if ( text == 'nothing' ) {
353
- this.stop();
354
- this.nothing();
355
- this.done();
356
- } else if ( text == 'continue' ) {
357
- this.kick();
358
- } else if ( this.mode = 'stopped' )
359
- jQuery(this.button).attr('value', strings.cont);
360
- }
361
- --this.kicks;
362
- },
363
- checkd: function(text, result) {
364
- if ( result == 'error' ) {
365
- // TODO: exception handling
366
- } else {
367
- eval('this.status='+text);
368
- jQuery('#pstat'+this.blog).empty().append(this.status.p1+'/'+this.status.p2);
369
- jQuery('#cstat'+this.blog).empty().append(this.status.c1+'/'+this.status.c2);
370
- this.update();
371
- if ( this.cont || this.kicks > 0 )
372
- setTimeout('blogs['+this.blog+'].check()', $interval);
373
- }
374
- --this.checks;
375
- },
376
- update: function() {
377
- 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');
378
- jQuery('#pstat'+this.blog).attr('title', 'Posts skipped '+this.status.p3);
379
- 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');
380
- jQuery('#cstat'+this.blog).attr('title', 'Comments skipped '+this.status.c3);
381
-
382
- },
383
- stop: function() {
384
- this.cont = false;
385
- },
386
- done: function() {
387
- this.mode = 'authors';
388
- jQuery(this.button).attr('value', strings.authors);
389
- },
390
- nothing: function() {
391
- this.mode = 'nothing';
392
- jQuery(this.button).remove();
393
- alert(strings.nothing);
394
- },
395
- getauthors: function() {
396
- if ( jQuery('div.wrap').length > 1 )
397
- jQuery('div.wrap').gt(0).remove();
398
- jQuery('div.wrap').empty().append('<h2>$authhead</h2><h3>' + this.title + '</h3>');
399
- jQuery('div.wrap').append('<p id=\"auth\">$loadauth</p>');
400
- jQuery('p#auth').load('index.php?import=blogger&noheader=true&authors=1',{blog:this.blog});
401
- },
402
- init: function() {
403
- this.update();
404
- var i = this.blog;
405
- jQuery(this.button).bind('click', function(){return blogs[i].click();});
406
- this.kicks = 0;
407
- this.checks = 0;
408
- },
409
- click: function() {
410
- if ( this.mode == 'init' || this.mode == 'stopped' || this.mode == 'posts' || this.mode == 'comments' ) {
411
- this.mode = 'started';
412
- this.start();
413
- jQuery(this.button).attr('value', strings.stop);
414
- } else if ( this.mode == 'started' ) {
415
- return false; // let it run...
416
- this.mode = 'stopped';
417
- this.stop();
418
- if ( this.checks > 0 || this.kicks > 0 ) {
419
- this.mode = 'stopping';
420
- jQuery(this.button).attr('value', strings.stopping);
421
- } else {
422
- jQuery(this.button).attr('value', strings.cont);
423
- }
424
- } else if ( this.mode == 'authors' ) {
425
- document.location = 'index.php?import=blogger&authors=1&blog='+this.blog;
426
- //this.mode = 'authors2';
427
- //this.getauthors();
428
- }
429
- return false;
430
- }
431
- };
432
- $init
433
- jQuery.each(blogs, function(i, me){me.init();});
434
- /* ]]> */
435
- </script>\n";
436
- }
437
-
438
- // Handy function for stopping the script after a number of seconds.
439
- function have_time()
440
- {
441
- global $importer_started;
442
- if (time() - $importer_started > MAX_EXECUTION_TIME)
443
- self::ajax_die('continue');
444
- return true;
445
- }
446
-
447
- function get_total_results($url)
448
- {
449
- $response = $this->oauth_get($url, array('max-results' => 1, 'start-index' => 2));
450
-
451
- $feed = new SimplePie();
452
- $feed->set_raw_data($response);
453
- $feed->init();
454
- $results = $feed->get_channel_tags('http://a9.com/-/spec/opensearchrss/1.0/', 'totalResults');
455
-
456
- $total_results = $results[0]['data'];
457
- unset($feed);
458
- return (int)$total_results;
459
- }
460
-
461
- function import_blog($blogID)
462
- {
463
- global $importing_blog;
464
- $importing_blog = $blogID;
465
-
466
- if (isset($_GET['authors']))
467
- return print ($this->get_author_form());
468
-
469
- if (isset($_GET['status']))
470
- self::ajax_die($this->get_js_status());
471
-
472
- if (isset($_GET['saveauthors']))
473
- self::ajax_die($this->save_authors());
474
-
475
- //Simpler counting for posts as we load them forwards
476
- if (isset($this->blogs[$importing_blog]['posts_start_index']))
477
- $start_index = (int)$this->blogs[$importing_blog]['posts_start_index'];
478
- else
479
- $start_index = 1;
480
-
481
- // This will be positive until we have finished importing posts
482
- if ($start_index > 0)
483
- {
484
- // Grab all the posts
485
- $this->blogs[$importing_blog]['mode'] = 'posts';
486
- do
487
- {
488
-
489
- $index = $struct = $entries = array();
490
-
491
- $url = $this->blogs[$importing_blog]['posts_url'];
492
-
493
- $response = $this->oauth_get($url, array('max-results' => MAX_RESULTS, 'start-index' => $start_index));
494
-
495
- if ($response == false)
496
- break;
497
-
498
- // parse the feed
499
- $feed = new SimplePie();
500
- $feed->set_item_class('WP_SimplePie_Blog_Item');
501
- $feed->set_sanitize_class('Blogger_Importer_Sanitize');
502
- $feed->set_raw_data($response);
503
- $feed->init();
504
-
505
- foreach ($feed->get_items() as $item)
506
- {
507
-
508
- $blogentry = new BloggerEntry();
509
-
510
- $blogentry->id = $item->get_id();
511
- $blogentry->published = $item->get_published();
512
- $blogentry->updated = $item->get_updated();
513
- $blogentry->isDraft = $item->get_draft_status($item);
514
- $blogentry->title = $item->get_title();
515
- $blogentry->content = $item->get_content();
516
- $blogentry->author = $item->get_author()->get_name();
517
- $blogentry->geotags = $item->get_geotags();
518
-
519
- $linktypes = array('replies', 'edit', 'self', 'alternate');
520
- foreach ($linktypes as $type)
521
- {
522
- $links = $item->get_links($type);
523
-
524
- if (!is_null($links))
525
- {
526
- foreach ($links as $link)
527
- {
528
- $blogentry->links[] = array('rel' => $type, 'href' => $link);
529
- }
530
- }
531
- }
532
-
533
- $cats = $item->get_categories();
534
-
535
- if (!is_null($cats))
536
- {
537
- foreach ($cats as $cat)
538
- {
539
- $blogentry->categories[] = $cat->term;
540
- }
541
- }
542
-
543
- $result = $this->import_post($blogentry);
544
-
545
- //Ref: Not importing properly http://core.trac.wordpress.org/ticket/19096
546
- //Simplified this section to count what is loaded rather than parsing the results again
547
- $start_index++;
548
- }
549
-
550
- $this->blogs[$importing_blog]['posts_start_index'] = $start_index;
551
-
552
- $this->save_vars();
553
-
554
- } while ($this->blogs[$importing_blog]['total_posts'] > $start_index && $this->have_time()); //have time function will "die" if it's out of time
555
- }
556
-
557
-
558
- if (isset($this->blogs[$importing_blog]['comments_start_index']))
559
- $start_index = (int)$this->blogs[$importing_blog]['comments_start_index'];
560
- else
561
- $start_index = 1;
562
-
563
- if ($start_index > 0 && $this->blogs[$importing_blog]['total_comments'] > 0)
564
- {
565
-
566
- $this->blogs[$importing_blog]['mode'] = 'comments';
567
- do
568
- {
569
- $index = $struct = $entries = array();
570
-
571
- //So we can link up the comments as we go we need to load them in reverse order
572
- //Reverse the start index as the GData Blogger feed can't be sorted
573
- $batch = ((floor(($this->blogs[$importing_blog]['total_comments'] - $start_index) / MAX_RESULTS) * MAX_RESULTS) + 1);
574
-
575
- $response = $this->oauth_get($this->blogs[$importing_blog]['comments_url'], array('max-results' => MAX_RESULTS, 'start-index' => $batch));
576
-
577
- // parse the feed
578
- $feed = new SimplePie();
579
- $feed->set_item_class('WP_SimplePie_Blog_Item');
580
- // Use the standard "stricter" sanitize class for comments
581
- $feed->set_raw_data($response);
582
- $feed->init();
583
-
584
- //Reverse the batch so we load the oldest comments first and hence can link up nested comments
585
- $comments = array_reverse($feed->get_items());
586
-
587
- if (!is_null($comments))
588
- {
589
- foreach ($comments as $item)
590
- {
591
-
592
- $blogentry = new BloggerEntry();
593
- $blogentry->id = $item->get_id();
594
- $blogentry->updated = $item->get_updated();
595
- $blogentry->content = $item->get_content();
596
- $blogentry->author = $item->get_author()->get_name();
597
- $blogentry->authoruri = $item->get_author()->get_link();
598
- $blogentry->authoremail = $item->get_author()->get_email();
599
-
600
- $temp = $item->get_item_tags('http://purl.org/syndication/thread/1.0', 'in-reply-to');
601
-
602
- foreach ($temp as $t)
603
- {
604
- if (isset($t['attribs']['']['source']))
605
- {
606
- $blogentry->source = $t['attribs']['']['source'];
607
- }
608
- }
609
-
610
- //Get the links
611
- $linktypes = array('edit', 'self', 'alternate', 'related');
612
- foreach ($linktypes as $type)
613
- {
614
- $links = $item->get_links($type);
615
- if (!is_null($links))
616
- {
617
- foreach ($links as $link)
618
- {
619
- $blogentry->links[] = array('rel' => $type, 'href' => $link);
620
- }
621
- }
622
- }
623
-
624
- $this->import_comment($blogentry);
625
- $start_index++;
626
- }
627
- }
628
-
629
- $this->blogs[$importing_blog]['comments_start_index'] = $start_index;
630
- $this->save_vars();
631
- } while ($this->blogs[$importing_blog]['total_comments'] > $start_index && $this->have_time());
632
- }
633
-
634
- $this->blogs[$importing_blog]['mode'] = 'authors';
635
- $this->save_vars();
636
-
637
- if (!$this->blogs[$importing_blog]['posts_done'] && !$this->blogs[$importing_blog]['comments_done'])
638
- self::ajax_die('nothing');
639
-
640
- do_action('import_done', 'blogger');
641
- self::ajax_die('done');
642
- }
643
-
644
- function no_apos($string)
645
- {
646
- return str_replace('&apos;', "'", $string);
647
- }
648
-
649
- function min_whitespace($string)
650
- {
651
- return preg_replace('|\s+|', ' ', $string);
652
- }
653
-
654
- function _normalize_tag($matches)
655
- {
656
- return '<' . strtolower($matches[1]);
657
- }
658
-
659
- function import_post($entry)
660
- {
661
- global $importing_blog;
662
-
663
- foreach ($entry->links as $link)
664
- {
665
- // save the self link as meta
666
- if ($link['rel'] == 'self')
667
- {
668
- $postself = $link['href'];
669
- $parts = parse_url($link['href']);
670
- $entry->old_permalink = $parts['path'];
671
- }
672
-
673
- // get the old URI for the page when available
674
- if ($link['rel'] == 'alternate')
675
- {
676
- $parts = parse_url($link['href']);
677
- $entry->bookmark = $parts['path'];
678
- }
679
-
680
- // save the replies feed link as meta (ignore the comment form one)
681
- if ($link['rel'] == 'replies' && false === strpos($link['href'], '#comment-form'))
682
- {
683
- $postreplies = $link['href'];
684
- }
685
- }
686
-
687
- //Check if we are double cleaning here? Does the Simplepie already do all this?
688
- $post_date = $entry->published;
689
- $post_content = trim(addslashes($this->no_apos(@html_entity_decode($entry->content, ENT_COMPAT, get_option('blog_charset')))));
690
- $post_title = trim(addslashes($this->no_apos($this->min_whitespace($entry->title))));
691
-
692
- $post_status = $entry->isDraft ? 'draft' : 'publish';
693
-
694
- // N.B. Clean up of $post_content is now part of the sanitize class
695
-
696
- // Checks for duplicates
697
- if (isset($this->blogs[$importing_blog]['posts'][$entry->old_permalink]))
698
- {
699
- $this->blogs[$importing_blog]['posts_skipped']++;
700
- } elseif ($post_id = post_exists($post_title, $post_content, $post_date))
701
- {
702
- $this->blogs[$importing_blog]['posts'][$entry->old_permalink] = $post_id;
703
- $this->blogs[$importing_blog]['posts_skipped']++;
704
- } else
705
- {
706
- $post = compact('post_date', 'post_content', 'post_title', 'post_status');
707
-
708
- $post_id = wp_insert_post($post);
709
- if (is_wp_error($post_id))
710
- return $post_id;
711
-
712
- wp_create_categories(array_map('addslashes', $entry->categories), $post_id);
713
-
714
- $author = $this->no_apos(strip_tags($entry->author));
715
-
716
- add_post_meta($post_id, 'blogger_blog', $this->blogs[$importing_blog]['host'], true);
717
- add_post_meta($post_id, 'blogger_author', $author, true);
718
-
719
- //Use the page id if available or the blogger internal id if it's a draft
720
- if ($entry->isDraft | !isset($entry->bookmark))
721
- add_post_meta($post_id, 'blogger_permalink', $entry->old_permalink, true);
722
- else
723
- add_post_meta($post_id, 'blogger_permalink', $entry->bookmark, true);
724
-
725
- add_post_meta($post_id, '_blogger_self', $postself, true);
726
-
727
- if (isset($entry->geotags)) {
728
- add_post_meta($post_id,'geo_latitude',$entry->geotags['geo_latitude']);
729
- add_post_meta($post_id,'geo_longitude',$entry->geotags['geo_longitude']);
730
- if (isset($entry->geotags['geo_address'])) {
731
- add_post_meta($post_id,'geo_address',$entry->geotags['geo_address']);
732
- }
733
- }
734
-
735
- $this->blogs[$importing_blog]['posts'][$entry->old_permalink] = $post_id;
736
-
737
- $this->blogs[$importing_blog]['posts_done']++;
738
- }
739
- $this->save_vars();
740
- return;
741
- }
742
-
743
- function import_comment($entry)
744
- {
745
- global $importing_blog;
746
-
747
- $parts = parse_url($entry->source);
748
- $entry->old_post_permalink = $parts['path']; //Will be something like this '/feeds/417730729915399755/posts/default/8397846992898424746'
749
-
750
- // Drop the #fragment and we have the comment's old post permalink.
751
- foreach ($entry->links as $link)
752
- {
753
- if ($link['rel'] == 'alternate')
754
- {
755
- $parts = parse_url($link['href']);
756
- $entry->old_permalink = $parts['fragment'];
757
- }
758
- //Parent post for nested links
759
- if ($link['rel'] == 'related')
760
- {
761
- $parts = parse_url($link['href']);
762
- $entry->related = $parts['path'];
763
- }
764
- if ($link['rel'] == 'self')
765
- {
766
- $parts = parse_url($link['href']);
767
- $entry->self = $parts['path'];
768
- }
769
- }
770
-
771
- //Check for duplicated cleanup here
772
- $comment_post_ID = (int)$this->blogs[$importing_blog]['posts'][$entry->old_post_permalink];
773
- $comment_author = addslashes($this->no_apos(strip_tags($entry->author)));
774
- $comment_author_url = addslashes($this->no_apos(strip_tags($entry->authoruri)));
775
- $comment_author_email = addslashes($this->no_apos(strip_tags($entry->authoremail)));
776
- $comment_date = $entry->updated;
777
-
778
- // Clean up content
779
- // Again, check if the Simplepie is already handling all of the cleaning
780
- $comment_content = addslashes($this->no_apos(@html_entity_decode($entry->content, ENT_COMPAT, get_option('blog_charset'))));
781
- $comment_content = preg_replace_callback('|<(/?[A-Z]+)|', array(&$this, '_normalize_tag'), $comment_content);
782
- $comment_content = str_replace('<br>', '<br />', $comment_content);
783
- $comment_content = str_replace('<hr>', '<hr />', $comment_content);
784
-
785
- // Nested comment?
786
- if (!is_null($entry->related))
787
- {
788
- $comment_parent = $this->blogs[$importing_blog]['comments'][$entry->related];
789
- }
790
-
791
- // if the post does not exist then we need stop and not add the comment
792
- if ($comment_post_ID != 0)
793
- {
794
- // Checks for duplicates
795
- if (isset($this->blogs[$importing_blog][$entry->id]) || $this->comment_exists($comment_post_ID, $comment_author, $comment_date))
796
- {
797
- $this->blogs[$importing_blog]['comments_skipped']++;
798
- } else
799
- {
800
- $comment = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_author_email', 'comment_date', 'comment_content', 'comment_parent');
801
-
802
- $comment = wp_filter_comment($comment);
803
- $comment_id = wp_insert_comment($comment);
804
-
805
- $this->blogs[$importing_blog]['comments'][$entry->id] = $comment_id;
806
- $this->blogs[$importing_blog]['comments'][$entry->self] = $comment_id; //For nested comments
807
-
808
- $this->blogs[$importing_blog]['comments_done']++;
809
- }
810
- } else
811
- {
812
- $this->blogs[$importing_blog]['comments_skipped']++;
813
- }
814
- $this->save_vars();
815
- }
816
-
817
- function ajax_die($data)
818
- {
819
- ob_clean(); //Discard any debug messages or other fluff already sent
820
- header('Content-Type: text/plain');
821
- die($data);
822
- }
823
-
824
-
825
- function get_js_status($blog = false)
826
- {
827
- global $importing_blog;
828
- if ($blog === false)
829
- $blog = $this->blogs[$importing_blog];
830
- else
831
- $blog = $this->blogs[$blog];
832
-
833
- $p1 = isset($blog['posts_done']) ? (int)$blog['posts_done'] : 0;
834
- $p2 = isset($blog['total_posts']) ? (int)$blog['total_posts'] : 0;
835
- $p3 = isset($blog['posts_skipped']) ? (int)$blog['posts_skipped'] : 0;
836
- $c1 = isset($blog['comments_done']) ? (int)$blog['comments_done'] : 0;
837
- $c2 = isset($blog['total_comments']) ? (int)$blog['total_comments'] : 0;
838
- $c3 = isset($blog['comments_skipped']) ? (int)$blog['comments_skipped'] : 0;
839
- return "{p1:$p1,p2:$p2,p3:$p3,c1:$c1,c2:$c2,c3:$c3}";
840
- }
841
-
842
- function get_author_form($blog = false)
843
- {
844
- global $importing_blog, $wpdb, $current_user;
845
- if ($blog === false)
846
- $blog = &$this->blogs[$importing_blog];
847
- else
848
- $blog = &$this->blogs[$blog];
849
-
850
- if (!isset($blog['authors']))
851
- {
852
- $post_ids = array_values($blog['posts']);
853
- $authors = (array )$wpdb->get_col("SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN (" . join(',', $post_ids) . ")");
854
- $blog['authors'] = array_map(null, $authors, array_fill(0, count($authors), $current_user->ID));
855
- $this->save_vars();
856
- }
857
-
858
- $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.',
859
- 'blogger-importer'), 'users.php');
860
- $heading = __('Author mapping', 'blogger-importer');
861
- $blogtitle = "{$blog['title']} ({$blog['host']})";
862
- $mapthis = __('Blogger username', 'blogger-importer');
863
- $tothis = __('WordPress login', 'blogger-importer');
864
- $submit = esc_js(__('Save Changes', 'blogger-importer'));
865
- $rows = '';
866
-
867
- foreach ($blog['authors'] as $i => $author)
868
- $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>";
869
-
870
- return "<div class='wrap'>".screen_icon()."<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='" .
871
- 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>";
872
- }
873
-
874
- function get_user_options($current)
875
- {
876
- global $importer_users;
877
- if (!isset($importer_users))
878
- $importer_users = (array )get_users(); //Function: get_users_of_blog() Deprecated in version 3.1. Use get_users() instead.
879
-
880
- $options = '';
881
-
882
- foreach ($importer_users as $user)
883
- {
884
- $sel = ($user->ID == $current) ? " selected='selected'" : '';
885
- $options .= "<option value='$user->ID'$sel>$user->display_name</option>";
886
- }
887
-
888
- return $options;
889
- }
890
-
891
- function save_authors()
892
- {
893
- global $importing_blog, $wpdb;
894
- $blog = &$this->blogs[$importing_blog]; //Get a reference to blogs so we don't have to write it longhand
895
-
896
- $authors = (array )$_POST['authors'];
897
-
898
- $host = $blog['host'];
899
-
900
- // Get an array of posts => authors
901
- $post_ids = (array )$wpdb->get_col($wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'blogger_blog' AND meta_value = %s", $host));
902
- $post_ids = join(',', $post_ids);
903
- $results = (array )$wpdb->get_results("SELECT post_id, meta_value FROM $wpdb->postmeta WHERE meta_key = 'blogger_author' AND post_id IN ($post_ids)");
904
- foreach ($results as $row)
905
- $authors_posts[$row->post_id] = $row->meta_value;
906
-
907
- foreach ($authors as $author => $user_id)
908
- {
909
- $user_id = (int)$user_id;
910
-
911
- // Skip authors that haven't been changed
912
- if ($user_id == $blog['authors'][$author][1])
913
- continue;
914
-
915
- // Get a list of the selected author's posts
916
- $post_ids = (array )array_keys($authors_posts, $blog['authors'][$author][0]);
917
- $post_ids = join(',', $post_ids);
918
-
919
- $wpdb->query($wpdb->prepare("UPDATE $wpdb->posts SET post_author = %d WHERE id IN ($post_ids)", $user_id));
920
- $blog['authors'][$author][1] = $user_id;
921
- }
922
- $this->save_vars();
923
-
924
- wp_redirect('edit.php');
925
- }
926
-
927
- function restart()
928
- {
929
- global $wpdb;
930
- $options = get_option('blogger_importer');
931
-
932
- if ( check_admin_referer( 'clear-blogger-importer', 'clear-blogger-importer-nonce' ) ) {
933
- delete_option('blogger_importer');
934
- $wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = 'blogger_author'");
935
- }
936
- wp_redirect('?import=blogger');
937
- exit;
938
- }
939
-
940
- // Step 9: Congratulate the user
941
- function congrats()
942
- {
943
- $blog = (int)$_GET['blog'];
944
- 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:',
945
- 'blogger-importer') . '</p><ul><li>' . __('That was hard work! Take a break.', 'blogger-importer') . '</li>';
946
- if (count($this->import['blogs']) > 1)
947
- 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>';
948
- if ($n = count($this->import['blogs'][$blog]['newusers']))
949
- 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.',
950
- 'blogger-importer'), 'users.php', '_parent') . '</li>';
951
- echo '<li>' . __('For security, click the link below to reset this importer.', 'blogger-importer') . '</li>';
952
- echo '</ul>';
953
- }
954
-
955
- // Figures out what to do, then does it.
956
- function start()
957
- {
958
- if (isset($_POST['restart']))
959
- $this->restart();
960
-
961
- $options = get_option('blogger_importer');
962
-
963
- if (is_array($options))
964
- foreach ($options as $key => $value)
965
- $this->$key = $value;
966
-
967
- if (isset($_REQUEST['blog']))
968
- {
969
- $blog = is_array($_REQUEST['blog']) ? array_shift($keys = array_keys($_REQUEST['blog'])) : $_REQUEST['blog'];
970
- $blog = (int)$blog;
971
- $result = $this->import_blog($blog);
972
- if (is_wp_error($result))
973
- echo $result->get_error_message();
974
- } elseif (isset($_GET['token']) && isset($_GET['token_secret']))
975
- $this->auth();
976
- elseif (isset($this->token) && isset($this->token_secret))
977
- $this->show_blogs();
978
- else
979
- $this->greet();
980
-
981
- $saved = $this->save_vars();
982
-
983
- if ($saved && !isset($_GET['noheader']))
984
- {
985
- $restart = __('Restart', 'blogger-importer');
986
- $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.',
987
- 'blogger-importer');
988
- $submit = esc_attr__('Clear account information', 'blogger-importer');
989
- echo "<div class='wrap'><h2>$restart</h2><p>$message</p><form method='post' action='?import=blogger&amp;noheader=true'>";
990
- wp_nonce_field( 'clear-blogger-importer', 'clear-blogger-importer-nonce' );
991
- echo "<p class='submit' style='text-align:left;'><input type='submit' class='button' value='$submit' name='restart' /></p></form></div>";
992
- }
993
- }
994
-
995
- function save_vars()
996
- {
997
- $vars = get_object_vars($this);
998
- update_option('blogger_importer', $vars);
999
-
1000
- return !empty($vars);
1001
- }
1002
-
1003
- function comment_exists($post_id, $comment_author, $comment_date)
1004
- {
1005
- //Do we have 2 comments for the same author at the same time, on the same post?
1006
- //returns comment id
1007
- global $wpdb;
1008
-
1009
- $comment_author = stripslashes($comment_author);
1010
- $comment_date = stripslashes($comment_date);
1011
-
1012
- return $wpdb->get_var($wpdb->prepare("SELECT comment_ID FROM $wpdb->comments
1013
- WHERE comment_post_ID = %s and comment_author = %s AND comment_date = %s", $post_id, $comment_author, $comment_date));
1014
- }
1015
-
1016
- }
1017
-
1018
- class BloggerEntry
1019
- {
1020
- var $links = array();
1021
- var $categories = array();
1022
- }
1023
-
1024
- } // class_exists( 'WP_Importer' )
1025
-
1026
- function blogger_importer_init()
1027
- {
1028
- load_plugin_textdomain('blogger-importer', false, dirname(plugin_basename(__file__)) . '/languages');
1029
-
1030
- $blogger_import = new Blogger_Import();
1031
- register_importer('blogger', __('Blogger', 'blogger-importer'), __('Import categories, posts and comments then maps users from a Blogger blog.', 'blogger-importer'), array($blogger_import, 'start'));
1032
-
1033
- }
1034
- add_action('admin_init', 'blogger_importer_init');
1
+ <?php
2
+
3
+ /*
4
+ Plugin Name: Blogger Importer
5
+ Plugin URI: http://wordpress.org/extend/plugins/blogger-importer/
6
+ Description: Imports posts, comments, images and tags from a Blogger blog then migrates authors to WordPress users.
7
+ Author: wordpressdotorg
8
+ Author URI: http://wordpress.org/
9
+ Version: 0.7
10
+ License: GPLv2
11
+ License URI: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
12
+ */
13
+
14
+ // Wordpress Classes needed by importer
15
+ require_once ABSPATH . 'wp-admin/includes/import.php';
16
+ if (!class_exists('WP_Importer'))
17
+ {
18
+ $class_wp_importer = ABSPATH . 'wp-admin/includes/class-wp-importer.php';
19
+ if (file_exists($class_wp_importer))
20
+ require_once $class_wp_importer;
21
+ }
22
+ if (!class_exists('WP_Error'))
23
+ {
24
+ $class_wp_error = ABSPATH . 'wp-includes/class-wp-error.php';
25
+ if (file_exists($class_wp_error))
26
+ require_once $class_wp_error;
27
+ }
28
+
29
+
30
+ require_once ABSPATH . WPINC . '/class-feed.php';
31
+
32
+ // Custom classes used by importer
33
+ require_once dirname( __FILE__ ) . '/blogger-importer-sanitize.php';
34
+ require_once dirname( __FILE__ ) . '/blogger-importer-blogitem.php';
35
+ require_once dirname( __FILE__ ) . '/blogger-importer-blog.php';
36
+ require_once dirname( __FILE__ ) . '/blogger-importer-connector.php';
37
+ require_once dirname( __FILE__ ) . '/blogger-entry.php';
38
+ require_once dirname( __FILE__ ) . '/comment-entry.php';
39
+ require_once dirname( __FILE__ ) . '/oauth.php';
40
+ require_once dirname( __FILE__ ) . '/blogger-importer-table.php';
41
+
42
+ /**
43
+ * Blogger Importer
44
+ *
45
+ * @package WordPress
46
+ * @subpackage Importer
47
+ */
48
+ if (class_exists('WP_Importer'))
49
+ {
50
+ class Blogger_Import extends WP_Importer
51
+ {
52
+ // 14/2/2013 switched from global defines to class constants
53
+ // These are used to change the behaviour of the importer from the default.
54
+ const MAX_RESULTS = 25; // How many records per GData query (int)
55
+ const MAX_EXECUTION_TIME = 20; // How many seconds to let the script run (int)
56
+ const STATUS_INTERVAL = 3; // How many seconds between status bar updates (int)
57
+ const IMPORT_IMG = true; // Should we import the images (boolean)
58
+ const REMOTE_TIMEOUT = 5; // How many seconds to wait until google responds (int)
59
+ const LARGE_IMAGE_SIZE = '1024'; // The size of large images downloaded (string)
60
+ const POST_PINGBACK = 0; // Turn off the post pingback, set to 1 to re-enabled(bool)
61
+ //N.B. Not all images will be this large and not all will be able to be remapped.
62
+
63
+ var $blogs = array();
64
+ var $connector;
65
+
66
+ function Blogger_Import()
67
+ {
68
+ if (isset($_GET['import']) && $_GET['import'] == 'blogger')
69
+ {
70
+ add_action('admin_print_scripts', array(&$this, 'queue_scripts'));
71
+ add_action('admin_print_styles', array(&$this, 'queue_style'));
72
+ add_filter('blogger_importer_congrats', array(&$this, 'congrats_options'), 10, 2);
73
+ //Looking at the Wordpress importer there appear to be quite a few "filters" there that perhaps should be standardised across all importers.
74
+ }
75
+ //Get Data
76
+ $this->read_options();
77
+ }
78
+
79
+ static function register_importer()
80
+ {
81
+ if (!defined('WP_LOAD_IMPORTERS'))
82
+ return;
83
+ //Moved in from blogger_importer_init
84
+ load_plugin_textdomain('blogger-importer', false, dirname(plugin_basename(__file__)) . '/languages');
85
+
86
+ $blogger_import = new Blogger_Import();
87
+ register_importer('blogger', __('Blogger', 'blogger-importer'), __('Import categories, posts, images and comments then maps users from a Blogger blog.', 'blogger-importer'), array($blogger_import,'start'));
88
+ }
89
+
90
+ function queue_scripts($hook)
91
+ {
92
+ $interval = self::STATUS_INTERVAL * 1000;
93
+ wp_enqueue_script('BloggerImporter', plugins_url('/blogger-importer.js', __file__), array('jquery','jquery-ui-progressbar'), '', true);
94
+ wp_localize_script('BloggerImporter', 'BL_strings', array('ajaxURL' => admin_url('admin-ajax.php'), 'cont' => esc_js(__('Continue', 'blogger-importer')), 'stop' => esc_js(__('Importing...', 'blogger-importer')), 'stopping' => '', 'authors' =>
95
+ esc_js(__('Set Authors', 'blogger-importer')), 'nothing' => esc_js(__('Nothing was imported. Had you already imported this blog?', 'blogger-importer')), 'loadauth' => esc_js(__('Preparing author mapping form...',
96
+ 'blogger-importer')), 'authhead' => esc_js(__('Final Step: Author Mapping', 'blogger-importer')), 'interval' => $interval));
97
+ }
98
+
99
+ function queue_style()
100
+ {
101
+ wp_enqueue_style('BloggerImporter', plugins_url('/blogger-importer.css', __file__));
102
+ }
103
+
104
+ // Shows the welcome screen and the magic auth link.
105
+ function greet()
106
+ {
107
+ $title = __('Import Blogger', 'blogger-importer');
108
+ $welcome = __('Howdy! This importer allows you to import posts and comments from your Blogger account into your WordPress site.', 'blogger-importer');
109
+ $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');
110
+ $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');
111
+ $errormsg = __('Error occurred getting OAuth tokens from Google', 'blogger-importer');
112
+
113
+ echo "
114
+ <div class='wrap'>
115
+ " . screen_icon() . "
116
+ <h2>$title</h2>
117
+ <p>$welcome</p><p>$prereqs</p><p>$stepone</p>";
118
+
119
+ $isconnected = $this->connector->connect(admin_url('admin-ajax.php?action=BL_auth'));
120
+ if (!is_wp_error($isconnected))
121
+ {
122
+ echo($this->connector->auth_form());
123
+ } else
124
+ {
125
+
126
+ echo '<p>' . $errormsg . '</p>';
127
+ echo '<p><pre>
128
+ ' . $isconnected->get_error_message() . '
129
+ </pre></p>';
130
+ }
131
+ }
132
+
133
+
134
+ function uh_oh($title, $message, $info)
135
+ {
136
+ echo "<div class='wrap'>";
137
+ screen_icon();
138
+ echo "<h2>$title</h2><p>$message</p><pre>$info</pre></div>";
139
+ }
140
+
141
+ /**
142
+ * Gets the list of blogs from blogger into an array $this->blogs[]
143
+ */
144
+ function get_blogs($iter = 0)
145
+ {
146
+ $xml = $this->connector->oauth_get('https://www.blogger.com/feeds/default/blogs');
147
+
148
+ // Give it a few retries... apparently this step often flakes out the first time.
149
+ if (empty($xml))
150
+ {
151
+ if ($iter < 3)
152
+ {
153
+ return $this->get_blogs($iter + 1);
154
+ } else
155
+ {
156
+ return false;
157
+ }
158
+ }
159
+
160
+ $feed = new SimplePie();
161
+ $feed->set_raw_data($xml);
162
+ $feed->init();
163
+
164
+ $i = 0;
165
+ foreach ($feed->get_items() as $item)
166
+ {
167
+ $blog = new Blogger_Importer_Blog();
168
+ $blog->ID = $i++;
169
+ //Perhaps these could be passed as parameters to the new blog object or the init defaults call?
170
+ $blog->title = $item->get_title();
171
+ $blog->summary = $item->get_description();
172
+
173
+ //ID is of the form tag:blogger.com,1999:blog-417730729915399755
174
+ //We need that number from the end
175
+ $rawid = explode('-', $item->get_id());
176
+ $blog->id = $rawid[count($rawid) - 1];
177
+
178
+ $parts = parse_url($item->get_link(0, 'alternate'));
179
+ $blog->host = $parts['host'];
180
+ $blog->gateway = $item->get_link(0, 'edit');
181
+ $blog->posts_url = $item->get_link(0, 'http://schemas.google.com/g/2005#post');
182
+ //AGC:20/4/2012 Developers guide suggests that the correct feed is located as follows
183
+ //See https://developers.google.com/blogger/docs/1.0/developers_guide_php
184
+ $blog->comments_url = "http://www.blogger.com/feeds/{$blog->id}/comments/default";
185
+
186
+ $blog->init_defaults($this->connector->get_total_results($blog->posts_url),$this->connector->get_total_results($blog->comments_url));
187
+
188
+ $this->blogs[] = $blog;
189
+ }
190
+
191
+ return true;
192
+ }
193
+
194
+ /**
195
+ * Shows the list of your bloger blogs as a table
196
+ * 25/1/2013 AGC: Moved the table rendering to be a wp_list_table
197
+ *
198
+ * @See Blogger_Import_List_Table
199
+ * @Link http://wpengineer.com/2426/wp_list_table-a-step-by-step-guide/
200
+ */
201
+ function show_blogs()
202
+ {
203
+ global $wp_importers;
204
+ $noscript = esc_html__('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.',
205
+ 'blogger-importer');
206
+ $title = esc_html__('Import Blogger', 'blogger-importer');
207
+ $refreshbutton = esc_attr__('Refresh blog list', 'blogger-importer');
208
+
209
+ $intro = $wp_importers['blogger'][1];
210
+
211
+ $init = '';
212
+ foreach ($this->blogs as $i => $blog)
213
+ {
214
+ $blogtitle = esc_js($blog->title);
215
+ $init .= "blogs[$i]=new blog($i,'$blogtitle','{$blog->mode}','" . $blog->get_js_status() . '\');';
216
+ }
217
+
218
+ echo screen_icon() . "<h2>" . $title . "</h2><noscript>" . $noscript . "</noscript>";
219
+
220
+ echo '<p>' . $intro . '</p>';
221
+
222
+ $myListTable = new Blogger_Import_List_Table(array('ajax' => true));
223
+ $myListTable->prepare_items($this->blogs, $init);
224
+ echo '<div id="BlogList" class="wrap">';
225
+ $myListTable->display();
226
+ echo '</div>';
227
+
228
+ //Refresh button
229
+ echo ("<form method='post' action='?import=blogger'>
230
+ <p class='submit' style='text-align:left;'>");
231
+ wp_nonce_field( 'blogger-importer-refresh', 'blogger-importer-refresh-nonce' );
232
+ printf("<input type='submit' class='button' value='%s' name='refresh' /></p></form>",$refreshbutton);
233
+ }
234
+
235
+ /**
236
+ * A clean return function for Ajax calls,
237
+ * discards any debug messages or other fluff already sent
238
+ * N.B. "headers already sent" errors occurring in debug, perhaps need to turn on output buffering??
239
+ * Note this might not be an issue with the switch to "proper" ajax handling
240
+ * http://www.dagondesign.com/articles/wordpress-hook-for-entire-page-using-output-buffering/
241
+ */
242
+
243
+ function ajax_die($data)
244
+ {
245
+ if (ob_get_level() != 0) {
246
+ ob_clean();
247
+ }
248
+
249
+ header( 'Content-Type: text/plain' );
250
+ echo $data;
251
+ exit;
252
+ }
253
+
254
+ //AJAX functions
255
+ static function ajax_getstatus()
256
+ {
257
+ $blogID = $_POST['blogID'];
258
+ $blog = Blogger_Importer_Blog::read_option($blogID);
259
+ Blogger_Import::ajax_die($blog->get_js_status());
260
+ }
261
+
262
+ static function ajax_doimport()
263
+ {
264
+ $blogID = $_POST['blogID'];
265
+ $blog = Blogger_Importer_Blog::read_option($blogID);
266
+ $connector = Blogger_Importer_Connector::read_option();
267
+ Blogger_Import::ajax_die($blog->import_blog($connector));
268
+ }
269
+
270
+ static function ajax_doauth()
271
+ {
272
+ $connector = Blogger_Importer_Connector::read_option();
273
+ $connector->auth($_GET['token'],$_GET['secret']);
274
+ wp_redirect(admin_url('admin.php?import=blogger'));
275
+ }
276
+
277
+ function get_author_form($blog)
278
+ {
279
+ global $current_user; //This is not used, perhaps it should be the "default" for the call to get_user_options?
280
+
281
+ if (!isset($blog->authors))
282
+ {
283
+ $blog->get_authors();
284
+ $blog->save_vars();
285
+ }
286
+
287
+ $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.',
288
+ 'blogger-importer'), 'users.php');
289
+ $heading = __('Author mapping', 'blogger-importer');
290
+ $blogtitle = "{$blog->title} ({$blog->host})";
291
+ $mapthis = __('Blogger username', 'blogger-importer');
292
+ $tothis = __('WordPress login', 'blogger-importer');
293
+ $submit = esc_js(__('Save Changes', 'blogger-importer'));
294
+ $rows = '';
295
+
296
+ foreach ($blog->authors as $i => $author)
297
+ $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>";
298
+
299
+
300
+
301
+ return "<div class='wrap'>" . screen_icon() . "<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='" .
302
+ esc_attr($blog->ID) . "' />".wp_nonce_field( 'blogger-importer-saveauthors', 'blogger-importer-saveauthors-nonce',true,false )."<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>";
303
+ }
304
+
305
+ function get_user_options($current)
306
+ {
307
+ //AGC: 21/10/2013 Simplified function, caching using a global variable not needed as this is not a function that is called frequently.
308
+ $importer_users = (array )get_users(); //Function: get_users_of_blog() Deprecated in version 3.1. Use get_users() instead.
309
+
310
+ $options = '';
311
+
312
+ foreach ($importer_users as $user)
313
+ {
314
+ $sel = ($user->ID == $current) ? " selected='selected'" : '';
315
+ $options .= "<option value='$user->ID'$sel>$user->display_name</option>";
316
+ }
317
+
318
+ return $options;
319
+ }
320
+
321
+ function restart()
322
+ {
323
+ $this->connector->reset();
324
+ $options = get_option('blogger_importer');
325
+
326
+ delete_option('blogger_importer');
327
+ foreach ($this->blogs as $i => $blog)
328
+ {
329
+ delete_option('blogger_importer_blog_'.$blog->ID);
330
+ }
331
+ }
332
+
333
+ // Step 9: Congratulate the user
334
+ function congrats()
335
+ {
336
+ echo "<div class='wrap'>";
337
+ screen_icon();
338
+
339
+ echo '<h2>' . __('Congratulations!', 'blogger-importer') . '</h2><p>' . __('Now that you have imported your Blogger blog into WordPress, what are you going to do? Here are some suggestions:',
340
+ 'blogger-importer') . '</p><ul>';
341
+
342
+ $congrats = apply_filters('blogger_importer_congrats', '', count($this->blogs));
343
+ $congrats = $congrats . '<li>' . __('For security, click the link below to reset this importer.', 'blogger-importer') . '</li>';
344
+ echo $congrats;
345
+ echo '</ul>';
346
+ echo '</div>';
347
+ }
348
+
349
+ function congrats_options($optionlist, $blogcount)
350
+ {
351
+ //Plugable list of options called by filter 'blogger_importer_congrats'
352
+ $optionlist = $optionlist . '<li><a href="' . admin_url('edit.php') . '">' . __('Review posts', 'blogger-importer') . '</a></li>';
353
+ $optionlist = $optionlist . '<li><a href="' . admin_url('edit-tags.php?taxonomy=category') . '">' . __('Review categories', 'blogger-importer') . '</a></li>';
354
+ $optionlist = $optionlist . '<li><a href="' . admin_url('import.php') . '">' . __('Convert categories to tags', 'blogger-importer') . '</a></li>';
355
+ $optionlist = $optionlist . '<li><a href="' . admin_url('edit-comments.php') . '">' . __('Review comments', 'blogger-importer') . '</a></li>';
356
+ $optionlist = $optionlist . '<li><a href="' . admin_url('upload.php') . '">' . __('Review media', 'blogger-importer') . '</a></li>';
357
+
358
+ if ($blogcount > 1)
359
+ {
360
+ $optionlist = $optionlist . '<li>' . __('In case you haven&#8217;t done it already, you can import the posts from your other blogs:', 'blogger-importer');
361
+ $optionlist = $optionlist . ' <a href="' . admin_url('?import=blogger') . '">' . __('Show blogs', 'blogger-importer') . '</a></li>';
362
+ }
363
+ return $optionlist;
364
+ }
365
+
366
+ function read_options()
367
+ {
368
+ $options = get_option('blogger_importer');
369
+ if (is_array($options))
370
+ foreach ($options as $key => $value)
371
+ $this->$key = $value;
372
+
373
+ $this->connector = Blogger_Importer_Connector::read_option();
374
+
375
+ if (count($this->blogs) == 0) {
376
+ $blog = true;
377
+ for ($i = 0; $blog ; $i++) {
378
+ $blog = Blogger_Importer_Blog::read_option($i);
379
+ if ($blog) {
380
+ $this->blogs[] = $blog;
381
+ }
382
+ }
383
+ }
384
+ }
385
+
386
+ function save_vars()
387
+ {
388
+ //Todo: return false if errors occur
389
+ $vars = get_object_vars($this);
390
+
391
+ if (array_key_exists('blogs',$vars)){
392
+ unset($vars['blogs']);
393
+ }
394
+ if (array_key_exists('connector',$vars)){
395
+ unset($vars['connector']);
396
+ }
397
+
398
+ //http://core.trac.wordpress.org/ticket/13480
399
+ //Calling update options multiple times in a page (or ajax call) means that the cache kicks in and does not save to DB
400
+ update_option('blogger_importer', $vars);
401
+
402
+ //How to check for errors here?
403
+ if (isset($connector)) {
404
+ $connector->save_vars();
405
+ }
406
+ foreach ($this->blogs as $i => $blog) {
407
+ $blog->save_vars();
408
+ }
409
+ return true;
410
+ }
411
+
412
+
413
+ /**
414
+ * The start function is what is called when the importer runs
415
+ * it is used to parse the parameters and select the appropriate
416
+ * action such as importing a blog
417
+ * Moved status and import out to separate ajax calls
418
+ */
419
+ function start()
420
+ {
421
+ if (isset($_POST['restart'])) {
422
+ if ( check_admin_referer( 'blogger-importer-clear', 'blogger-importer-clear-nonce' ) ) {
423
+ $this->restart();
424
+ wp_redirect('?import=blogger');
425
+ } else {
426
+ wp_die('Error');
427
+ }
428
+ }
429
+ if (isset($_POST['refresh'])) {
430
+ if ( check_admin_referer( 'blogger-importer-refresh', 'blogger-importer-refresh-nonce' ) ) {
431
+ $this->blogs = array();
432
+ } else {
433
+ wp_die('Error');
434
+ }
435
+ }
436
+ if (isset($_REQUEST['blog'])) {
437
+ $importing_blog = (int)(is_array($_REQUEST['blog']) ? array_shift($keys = array_keys($_REQUEST['blog'])) : $_REQUEST['blog']);
438
+ $blog = $this->blogs[$importing_blog];
439
+
440
+ if (isset($_GET['authors'])) {
441
+ print ($this->get_author_form($blog));
442
+ return;
443
+ }
444
+ if (isset($_GET['saveauthors'])) {
445
+ if ( check_admin_referer( 'blogger-importer-saveauthors', 'blogger-importer-saveauthors-nonce' )) {
446
+ $blog->save_authors();
447
+ wp_redirect('?import=blogger&congrats=1');
448
+ }
449
+ }
450
+ } elseif (isset($_GET['congrats'])) {
451
+ $this->congrats();
452
+ } elseif (isset($this->connector) && $this->connector->isconnected()) {
453
+ if (empty($this->blogs)) {
454
+ if (!$this->get_blogs()) {
455
+ $this->uh_oh(__('Trouble signing in', 'blogger-importer'), __('We were not able to gain access to your account. Try starting over.', 'blogger-importer'), '');
456
+ }
457
+ if (empty($this->blogs)) {
458
+ $this->uh_oh(__('No blogs found', 'blogger-importer'), __('We were able to log in but there were no blogs. Try a different account next time.', 'blogger-importer'), '');
459
+ }
460
+ }
461
+ if (!empty($this->blogs)) {
462
+ $this->show_blogs();
463
+ }
464
+ } else {
465
+ $this->connector = new Blogger_Importer_Connector();
466
+ $this->greet();
467
+ }
468
+
469
+ if (isset($this->connector) && $this->connector->isconnected())
470
+ {
471
+ $restart = __('Restart', 'blogger-importer');
472
+ $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.',
473
+ 'blogger-importer');
474
+ $submit = esc_attr__('Clear account information', 'blogger-importer');
475
+ echo "<div class='wrap'><h2>$restart</h2><p>$message</p>";
476
+ echo "<form method='post' action='?import=blogger&amp;noheader=true'>";
477
+ wp_nonce_field( 'blogger-importer-clear', 'blogger-importer-clear-nonce' );
478
+ printf("<p class='submit' style='text-align:left;'><input type='submit' class='button' value='%s' name='restart' /></p></form>",$submit);
479
+ }
480
+ $this->save_vars();
481
+ }
482
+
483
+ function _log( $message ) {
484
+ //Log to file only, we can't log to display as this is a background(ajax) call and there is no display
485
+ if( WP_DEBUG === true && WP_DEBUG_DISPLAY === false ){
486
+ if( is_array( $message ) || is_object( $message ) ){
487
+ error_log( print_r( $message, true ) );
488
+ } else {
489
+ error_log( $message );
490
+ }
491
+ }
492
+ }
493
+ }
494
+ } // class_exists( 'WP_Importer' )
495
+
496
+ add_action('admin_init', array('Blogger_Import','register_importer'));
497
+ //Ajax calls
498
+ add_action('wp_ajax_BL_import', array('Blogger_Import','ajax_doimport'));
499
+ add_action('wp_ajax_BL_status', array('Blogger_Import','ajax_getstatus'));
500
+ add_action('wp_ajax_BL_auth', array('Blogger_Import','ajax_doauth'));
501
+
502
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
comment-entry.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @author Andy Clark
5
+ * @copyright 2013
6
+ * A data object representing the data to be added into Wordpress
7
+ */
8
+
9
+ if (!class_exists('CommentEntry'))
10
+ {
11
+ class CommentEntry {
12
+
13
+ var $links = array();
14
+ var $categories = array();
15
+
16
+ function parselinks() {
17
+ // Drop the #fragment and we have the comment's old post permalink.
18
+ foreach ($this->links as $link)
19
+ {
20
+ if ($link['rel'] == 'alternate')
21
+ {
22
+ $parts = parse_url($link['href']);
23
+ $this->old_permalink = $parts['fragment'];
24
+ }
25
+ //Parent post for nested links
26
+ if ($link['rel'] == 'related')
27
+ {
28
+ $parts = parse_url($link['href']);
29
+ $this->related = $parts['path'];
30
+ }
31
+ if ($link['rel'] == 'self')
32
+ {
33
+ $parts = parse_url($link['href']);
34
+ $this->self = $parts['path'];
35
+ }
36
+ }
37
+ }
38
+
39
+ function import() {
40
+
41
+ $comment_author = $this->author;
42
+ $comment_author_url = $this->authoruri;
43
+ $comment_author_email = $this->authoremail;
44
+ $comment_date = $this->updated;
45
+
46
+ $comment_content = $this->content;
47
+ $comment_post_ID = $this->post_ID;
48
+ $comment_author_IP = '127.0.0.1'; //Blogger does not supply the IP so default this
49
+
50
+ // Clean up content
51
+ // Simplepie does some cleaning but does not do these.
52
+ $comment_content = str_replace('<br>', '<br />', $comment_content);
53
+ $comment_content = str_replace('<hr>', '<hr />', $comment_content);
54
+
55
+ $comment_parent = isset($this->parentcommentid) ? $this->parentcommentid : 0;
56
+
57
+ $comment = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_author_email','comment_author_IP','comment_date', 'comment_content', 'comment_parent');
58
+
59
+ $comment = wp_filter_comment($comment);
60
+ $comment_id = wp_insert_comment($comment);
61
+
62
+ //links of the form /feeds/417730729915399755/8397846992898424746/comments/default/7732208643735403000
63
+ add_comment_meta($comment_id, 'blogger_internal', $this->self, true);
64
+
65
+ return $comment_id;
66
+ }
67
+
68
+ function exists()
69
+ {
70
+ //Do we have 2 comments for the same author at the same time, on the same post?
71
+ //returns comment id
72
+
73
+ //Updated to first check the internal id
74
+ //Can we use get_comments here?
75
+ global $wpdb;
76
+ $c = $this->get_comment_by_oldID($this->self);
77
+ if ($c <> 0) {
78
+ return ($c);
79
+ }
80
+ else {
81
+ $commentID = $wpdb->get_var($wpdb->prepare("SELECT comment_ID FROM $wpdb->comments
82
+ WHERE comment_post_ID = %s and comment_author = %s AND comment_date = %s",
83
+ $this->post_ID, $this->author, $this->updated));
84
+ return ($commentID);
85
+ }
86
+ }
87
+
88
+ function get_comment_by_oldID($oldID) {
89
+ //Check to see if this post has been loaded already
90
+ //Can we use get_comments for this?
91
+ global $wpdb;
92
+ $query = "SELECT c.comment_id FROM $wpdb->commentmeta m inner join $wpdb->comments c on c.comment_ID = m.comment_id where meta_key = 'blogger_internal' and meta_value = '%s' LIMIT 0 , 1";
93
+ $c = (int) $wpdb->get_var( $wpdb->prepare($query, $oldID) );
94
+ return $c;
95
+ }
96
+
97
+
98
+
99
+ }
100
+ }
101
+
102
+ ?>
languages/blogger-importer-el.mo ADDED
Binary file
languages/blogger-importer-el.po ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: Blogger Importer 0.2\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2013-10-29 12:10-0000\n"
11
+ "PO-Revision-Date: 2013-10-29 15:45+0200\n"
12
+ "Last-Translator: Stergatu Lena <stergatu@cti.gr>\n"
13
+ "Language-Team: \n"
14
+ "Language: el_GR\n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "X-Poedit-SourceCharset: utf-8\n"
19
+ "X-Generator: Poedit 1.5.7\n"
20
+
21
+ #: ../blogger-importer-blog.php:462 ../blogger-importer-blog.php:787
22
+ msgid "Attempting to write back empty content"
23
+ msgstr "Προσπάθειας εγγραφής κενού περιεχομένου"
24
+
25
+ #: ../blogger-importer-blog.php:502 ../blogger-importer-blog.php:505
26
+ #: ../blogger-importer-table.php:55 ../blogger-importer-table.php:59
27
+ #, php-format
28
+ msgid "%d of %d"
29
+ msgstr "%d από %d"
30
+
31
+ #: ../blogger-importer-blog.php:503
32
+ #, php-format
33
+ msgid "%d posts skipped"
34
+ msgstr "%d άρθρα αγνοήθηκαν"
35
+
36
+ #: ../blogger-importer-blog.php:506
37
+ #, php-format
38
+ msgid "%d comments skipped"
39
+ msgstr "%d σχόλια αγνοήθηκαν"
40
+
41
+ #: ../blogger-importer-blog.php:508
42
+ #, php-format
43
+ msgid "%d images skipped"
44
+ msgstr "%d εικόνες αγνοήθηκαν"
45
+
46
+ #: ../blogger-importer-blog.php:695
47
+ msgid "Lowrez not an image"
48
+ msgstr "Lowrez δεν είναι εικόνα"
49
+
50
+ #: ../blogger-importer-blog.php:731
51
+ msgid "None of the images are valid"
52
+ msgstr "Καμία από τις εικόνες δεν είναι έγκυρη"
53
+
54
+ #: ../blogger-importer-table.php:19
55
+ msgid "Blog Name"
56
+ msgstr "Όνομα Ιστολογίου"
57
+
58
+ #: ../blogger-importer-table.php:20
59
+ msgid "Blog URL"
60
+ msgstr "URL Ιστολογίου"
61
+
62
+ #: ../blogger-importer-table.php:21
63
+ msgid "Posts"
64
+ msgstr "Άρθρα"
65
+
66
+ #: ../blogger-importer-table.php:22
67
+ msgid "Comments"
68
+ msgstr "Σχόλια"
69
+
70
+ #: ../blogger-importer-table.php:23
71
+ msgid "Images"
72
+ msgstr "Εικόνες"
73
+
74
+ #: ../blogger-importer-table.php:24
75
+ msgid "Links"
76
+ msgstr "Σύνδεσμοι"
77
+
78
+ #: ../blogger-importer-table.php:25
79
+ msgid "Action Button"
80
+ msgstr "Κουμπί ενέργειας"
81
+
82
+ #: ../blogger-importer-table.php:71
83
+ msgid "Import"
84
+ msgstr "Εισαγωγή"
85
+
86
+ #: ../blogger-importer-table.php:72 ../blogger-importer.php:84
87
+ msgid "Continue"
88
+ msgstr "Συνέχεια"
89
+
90
+ #: ../blogger-importer-table.php:73 ../blogger-importer.php:84
91
+ msgid "Importing..."
92
+ msgstr "Εισαγωγή…"
93
+
94
+ #: ../blogger-importer-table.php:74 ../blogger-importer.php:85
95
+ msgid "Set Authors"
96
+ msgstr "Ορισμός συντακτών"
97
+
98
+ #: ../blogger-importer.php:77
99
+ msgid "Blogger"
100
+ msgstr "Blogger"
101
+
102
+ #: ../blogger-importer.php:77
103
+ msgid ""
104
+ "Import categories, posts, images and comments then maps users from a Blogger "
105
+ "blog."
106
+ msgstr ""
107
+ "Εισαγωγή κατηγοριών, άρθρων, εικόνων και σχολίων και στην συνέχεια "
108
+ "αντιστοίχιση μελών από ιστολόγιο Blogger."
109
+
110
+ #: ../blogger-importer.php:85
111
+ msgid "Nothing was imported. Had you already imported this blog?"
112
+ msgstr "Δεν εισήχθη τίποτα. Μήπως είχατε εισάγει ήδη το ιστολόγιο;"
113
+
114
+ #: ../blogger-importer.php:85
115
+ msgid "Preparing author mapping form..."
116
+ msgstr "Προετοιμασία χάρτη συντακτών…"
117
+
118
+ #: ../blogger-importer.php:86
119
+ msgid "Final Step: Author Mapping"
120
+ msgstr "Τελευταίο βήμα: Χάρτης συντακτών"
121
+
122
+ #: ../blogger-importer.php:99
123
+ msgid "Import Blogger"
124
+ msgstr "Εισαγωγή Blogger"
125
+
126
+ #: ../blogger-importer.php:100
127
+ msgid ""
128
+ "Howdy! This importer allows you to import posts and comments from your "
129
+ "Blogger account into your WordPress site."
130
+ msgstr ""
131
+ "Καλώς ήρθατε! Ο εισαγωγέας αυτός είναι για να εισάγετε άρθρα και σχόλια από "
132
+ "λογαριασμό Blogger σε ένα ιστολόγιο WordPress."
133
+
134
+ #: ../blogger-importer.php:101
135
+ msgid ""
136
+ "To use this importer, you must have a Google account and an upgraded (New, "
137
+ "was Beta) blog hosted on blogspot.com or a custom domain (not FTP)."
138
+ msgstr ""
139
+ "Για να χρησιμοποιήσετε τον εισαγωγέα, πρέπει να έχετε λογαριασμό Google και "
140
+ "αναβαθμισμένο ιστολόγιο στο blogspot.com ή σε δικό σας όνομα (όχι FTP)."
141
+
142
+ #: ../blogger-importer.php:102
143
+ msgid ""
144
+ "The first thing you need to do is tell Blogger to let WordPress access your "
145
+ "account. You will be sent back here after providing authorization."
146
+ msgstr ""
147
+ "Καταρχάς, πρέπει να πείτε στο Blogger να δώσει πρόσβαση στον λογαριασμό του "
148
+ "WordPress. Αφού δώσετε την έγκριση, θα επιστρέψετε αυτόματα εδώ."
149
+
150
+ #: ../blogger-importer.php:104
151
+ msgid "Error occurred getting OAuth tokens from Google"
152
+ msgstr "Σφάλμα ανάκτησης OAuth tokens από την Google"
153
+
154
+ #: ../blogger-importer.php:142
155
+ msgid ""
156
+ "OpenSSL is not installed check your PHP.INI or ask your server provider to "
157
+ "enable the OpenSSL module."
158
+ msgstr ""
159
+ "Το OpenSSL δεν είναι εγκατεστημένο. Ελέξτε το PHP.INI ή ζητήστε από τον "
160
+ "server provider να ενεργοποιήσει το OpenSSL module."
161
+
162
+ #: ../blogger-importer.php:175
163
+ msgid ""
164
+ "Invalid Token: Check server date, firewall settings and transports (curl, "
165
+ "streams and fsockopen)"
166
+ msgstr ""
167
+ "Μη έγκυρο Token: Ελέξτε την ημερομηνία του server, τις ρυθμίσεις firewall "
168
+ "και μεταφοράς δεδομένων (curl, streams and fsockopen)"
169
+
170
+ #: ../blogger-importer.php:394
171
+ #, php-format
172
+ msgid ""
173
+ "All posts were imported with the current user as author. Use this form to "
174
+ "move each Blogger user&#8217;s posts to a different WordPress user. You may "
175
+ "<a href=\"%s\">add users</a> and then return to this page and complete the "
176
+ "user mapping. This form may be used as many times as you like until you "
177
+ "activate the &#8220;Restart&#8221; function below."
178
+ msgstr ""
179
+ "Όλα τα άρθρα εισήχθησαν με συντάκτη τον τρέχοντα χρήστη. Χρησιμοποιήστε τη "
180
+ "φόρμα για να μεταφέρετε τα άρθρα κάθε συντάκτη του Blogger σε διαφορετικό "
181
+ "συντάκτη του WordPress. Μπορείτε να <a href=\"%s\">προσθέσετε μέλη</a> και "
182
+ "να επιστρέψετε για να ολοκληρώσετε την αναχαρτογράφηση. Μέχρι να "
183
+ "ενεργοποιήσετε την «Επανεκκίνηση», γίνεται να χρησιμοποιήσετε τη φόρμα όσες "
184
+ "φορές θέλετε."
185
+
186
+ #: ../blogger-importer.php:396
187
+ msgid "Author mapping"
188
+ msgstr "Χάρτης συντακτών"
189
+
190
+ #: ../blogger-importer.php:398
191
+ msgid "Blogger username"
192
+ msgstr "Όνομα χρήστη Blogger"
193
+
194
+ #: ../blogger-importer.php:399
195
+ msgid "WordPress login"
196
+ msgstr "Σύνδεση WordPress"
197
+
198
+ #: ../blogger-importer.php:400
199
+ msgid "Save Changes"
200
+ msgstr "Αποθήκευση αλλαγών"
201
+
202
+ #: ../blogger-importer.php:447
203
+ msgid "Congratulations!"
204
+ msgstr "Συγχαρητήρια!"
205
+
206
+ #: ../blogger-importer.php:447
207
+ msgid ""
208
+ "Now that you have imported your Blogger blog into WordPress, what are you "
209
+ "going to do? Here are some suggestions:"
210
+ msgstr ""
211
+ "Η εισαγωγή του ιστολογίου Blogger στο WordPress τελείωσε. Τι θα κάνετε τώρα; "
212
+ "Να μερικές ιδέες:"
213
+
214
+ #: ../blogger-importer.php:451
215
+ msgid "For security, click the link below to reset this importer."
216
+ msgstr "Για λόγους ασφαλείας, κάντε κλικ πιο κάτω να μηδενίσετε τον εισαγωγέα."
217
+
218
+ #: ../blogger-importer.php:460
219
+ msgid "Review posts"
220
+ msgstr "Ανασκόπηση άρθρων"
221
+
222
+ #: ../blogger-importer.php:461
223
+ msgid "Review categories"
224
+ msgstr "Ανασκόπηση ετικετών"
225
+
226
+ #: ../blogger-importer.php:462
227
+ msgid "Convert categories to tags"
228
+ msgstr "Μετατροπή κατηγοριών σε ετικέτες"
229
+
230
+ #: ../blogger-importer.php:463
231
+ msgid "Review comments"
232
+ msgstr "Ανασκόπηση σχολίων"
233
+
234
+ #: ../blogger-importer.php:464
235
+ msgid "Review media"
236
+ msgstr "Ανασκόπηση πολυμέσων"
237
+
238
+ #: ../blogger-importer.php:468
239
+ msgid ""
240
+ "In case you haven&#8217;t done it already, you can import the posts from "
241
+ "your other blogs:"
242
+ msgstr ""
243
+ "Αν δεν το έχετε κάνει ήδη, μπορείτε να εισάγετε άρθρα από άλλα ιστολόγια σας:"
244
+
245
+ #: ../blogger-importer.php:469
246
+ msgid "Show blogs"
247
+ msgstr "Προβολή ιστολογίων"
248
+
249
+ #: ../blogger-importer.php:554
250
+ msgid "Trouble signing in"
251
+ msgstr "Πρόβλημα στη σύνδεση"
252
+
253
+ #: ../blogger-importer.php:554
254
+ msgid "We were not able to gain access to your account. Try starting over."
255
+ msgstr ""
256
+ "Δεν ήταν δυνατή η πρόσβαση στον λογαριασμό σας. Δοκιμάστε άλλη μία φορά."
257
+
258
+ #: ../blogger-importer.php:557
259
+ msgid "No blogs found"
260
+ msgstr "Δεν βρέθηκαν ιστολόγια"
261
+
262
+ #: ../blogger-importer.php:557
263
+ msgid ""
264
+ "We were able to log in but there were no blogs. Try a different account next "
265
+ "time."
266
+ msgstr ""
267
+ "Η είσοδος έγινε κανονικά αλλά δεν βρέθηκε ιστολόγιο. Δοκιμάστε έναν άλλο "
268
+ "λογαριασμό."
269
+
270
+ #: ../blogger-importer.php:568
271
+ msgid "Restart"
272
+ msgstr "Επανεκκίνηση"
273
+
274
+ #: ../blogger-importer.php:569
275
+ msgid ""
276
+ "We have saved some information about your Blogger account in your WordPress "
277
+ "database. Clearing this information will allow you to start over. Restarting "
278
+ "will not affect any posts you have already imported. If you attempt to re-"
279
+ "import a blog, duplicate posts and comments will be skipped."
280
+ msgstr ""
281
+ "Αποθηκεύσαμε στη βάση δεδομένων του WordPress κάποια στοιχεία από τον "
282
+ "λογαριασμό σας στο Blogger. Η αφαίρεση αυτών των στοιχείων σας επιτρέπει να "
283
+ "ξεκινήσετε από την αρχή. Άρθρα που έχουν ήδη εισαχθεί δεν επηρεάζονται. Αν "
284
+ "επιχειρήσετε να επανεισάγετε ένα ιστολόγιο, τα ήδη εισηγμένα άρθρα και "
285
+ "σχόλια δεν επανεισάγονται."
286
+
287
+ #~ msgid "Authorize"
288
+ #~ msgstr "Εξουσιοδότηση"
289
+
290
+ #~ msgid "Blogger Blogs"
291
+ #~ msgstr "Ιστολόγια Blogger"
292
+
293
+ #~ msgid "The Magic Button"
294
+ #~ msgstr "Το Μαγικό Κουμπί"
295
+
296
+ #~ msgid ""
297
+ #~ "This feature requires Javascript but it seems to be disabled. Please "
298
+ #~ "enable Javascript and then reload this page. Don&#8217;t worry, you can "
299
+ #~ "turn it back off when you&#8217;re done."
300
+ #~ msgstr ""
301
+ #~ "Η λειτουργία αυτή απαιτεί JavaScript, που φαίνεται ότι είναι "
302
+ #~ "απενεργοποιημένη. Παρακαλούμε ενεργοποιήστε την JavaScript και ανανεώστε "
303
+ #~ "τη σελίδα. (Μόλις τελειώσετε, μπορείτε να απενεργοποιήσετε πάλι την "
304
+ #~ "JavaScript.)"
305
+
306
+ #~ msgid "That was hard work! Take a break."
307
+ #~ msgstr "Ήταν κουραστική εργασία! Κάντε ένα διάλειμμα!"
308
+
309
+ #~ msgid ""
310
+ #~ "Go to <a href=\"%s\" target=\"%s\">Authors &amp; Users</a>, where you can "
311
+ #~ "modify the new user(s) or delete them. If you want to make all of the "
312
+ #~ "imported posts yours, you will be given that option when you delete the "
313
+ #~ "new authors."
314
+ #~ msgstr ""
315
+ #~ "Στο <a href=\"%s\" target=\"%s\">Συντάκτες &amp; Μέλη</a> μπορείτε να "
316
+ #~ "επεξεργαστείτε νέα μέλη ή να τα διαγράψετε. Αν θέλετε να μεταφέρετε στον "
317
+ #~ "δικό σας λογαριασμό όλα τα εισηγμένα άρθρα, σας δίνεται η επιλογή όταν "
318
+ #~ "διαγράφετε τους νέους συντάκτες."
319
+
320
+ #~ msgid "Clear account information"
321
+ #~ msgstr "Καθαρισμός πληροφοριών λογαριασμού"
322
+
323
+ #~ msgid "Blogger Importer"
324
+ #~ msgstr "Εισαγωγέας Blogger"
325
+
326
+ #~ msgid "http://wordpress.org/extend/plugins/blogger-importer/"
327
+ #~ msgstr "http://wordpress.org/extend/plugins/blogger-importer/"
328
+
329
+ #~ msgid ""
330
+ #~ "Import posts, comments and tags from a Blogger blog and migrate authors "
331
+ #~ "to WordPress users."
332
+ #~ msgstr ""
333
+ #~ "Εισαγωγή άρθρων, σχολίων και ετικετών από ιστολόγιο Blogger και στην "
334
+ #~ "συνέχεια αντιστοίχιση συντακτών με μέλη του ιστολογίου σας."
335
+
336
+ #~ msgid "wordpressdotorg"
337
+ #~ msgstr "wordpressdotorg"
338
+
339
+ #~ msgid "http://wordpress.org/"
340
+ #~ msgstr "http://wordpress.org/"
341
+
342
+ #~ msgid "Authorization failed"
343
+ #~ msgstr "Εξουσιοδότηση απέτυχε"
344
+
345
+ #~ msgid ""
346
+ #~ "Something went wrong. If the problem persists, send this info to support:"
347
+ #~ msgstr ""
348
+ #~ "Κάτι δεν πήγε καλά. Αν επιμείνει το πρόβλημα, στείλτε αυτά τα στοιχεία "
349
+ #~ "στην υποστήριξη:"
350
+
351
+ #~ msgid "Could not connect to https://www.google.com"
352
+ #~ msgstr "Αδύνατη η σύνδεση με https://www.google.com"
353
+
354
+ #~ msgid ""
355
+ #~ "There was a problem opening a secure connection to Google. This is what "
356
+ #~ "went wrong:"
357
+ #~ msgstr ""
358
+ #~ "Δεν ήταν δυνατή η ασφαλής σύνδεση με το Google. Να τι δεν πήγε καλά:"
359
+
360
+ #~ msgid "Could not connect to %s"
361
+ #~ msgstr "Αδύνατη η σύνδεση με %s"
362
+
363
+ #~ msgid ""
364
+ #~ "There was a problem opening a connection to Blogger. This is what went "
365
+ #~ "wrong:"
366
+ #~ msgstr "Δεν ήταν δυνατή η σύνδεση με το Blogger. Να τι δεν πήγε καλά:"
367
+
368
+ #~ msgid "Import posts, comments, tags, and attachments from a Blogger blog."
369
+ #~ msgstr ""
370
+ #~ "Εγκαταστήστε τον εισαγωγέα Blogger για να εισάγετε άρθρα, σχόλια και μέλη "
371
+ #~ "ιστολόγιο Blogger."
languages/blogger-importer.pot CHANGED
@@ -2,219 +2,232 @@
2
  # This file is distributed under the same license as the Blogger Importer package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Blogger Importer 0.5\n"
6
- "Report-Msgid-Bugs-To: http://wordpress.org/tag/blogger-importer\n"
7
- "POT-Creation-Date: 2012-06-13 16:29:40+00:00\n"
 
 
 
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2012-MO-DA HO:MI+ZONE\n"
12
- "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
- "Language-Team: LANGUAGE <LL@li.org>\n"
14
 
15
- #: blogger-importer.php:101
16
- msgid "Import Blogger"
 
17
  msgstr ""
18
 
19
- #: blogger-importer.php:102
20
- msgid ""
21
- "Howdy! This importer allows you to import posts and comments from your "
22
- "Blogger account into your WordPress site."
 
 
23
  msgstr ""
24
 
25
- #: blogger-importer.php:103
26
- msgid ""
27
- "To use this importer, you must have a Google account and an upgraded (New, "
28
- "was Beta) blog hosted on blogspot.com or a custom domain (not FTP)."
29
  msgstr ""
30
 
31
- #: blogger-importer.php:104
32
- msgid ""
33
- "The first thing you need to do is tell Blogger to let WordPress access your "
34
- "account. You will be sent back here after providing authorization."
35
  msgstr ""
36
 
37
- #: blogger-importer.php:105
38
- msgid "Authorize"
 
39
  msgstr ""
40
 
41
- #: blogger-importer.php:225
42
- msgid "Trouble signing in"
43
  msgstr ""
44
 
45
- #: blogger-importer.php:225
46
- msgid "We were not able to gain access to your account. Try starting over."
47
  msgstr ""
48
 
49
- #: blogger-importer.php:267
50
- msgid "No blogs found"
51
  msgstr ""
52
 
53
- #: blogger-importer.php:267
54
- msgid ""
55
- "We were able to log in but there were no blogs. Try a different account next "
56
- "time."
57
  msgstr ""
58
 
59
- #: blogger-importer.php:275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  msgid "Import"
61
  msgstr ""
62
 
63
- #: blogger-importer.php:276
 
64
  msgid "Continue"
65
  msgstr ""
66
 
67
- #: blogger-importer.php:277
 
68
  msgid "Importing..."
69
  msgstr ""
70
 
71
- #: blogger-importer.php:278
 
72
  msgid "Set Authors"
73
  msgstr ""
74
 
75
- #: blogger-importer.php:279
76
- msgid "Preparing author mapping form..."
77
  msgstr ""
78
 
79
- #: blogger-importer.php:280
80
- msgid "Final Step: Author Mapping"
81
  msgstr ""
82
 
83
- #: blogger-importer.php:281
84
  msgid "Nothing was imported. Had you already imported this blog?"
85
  msgstr ""
86
 
87
- #: blogger-importer.php:283
88
- msgid "Blogger Blogs"
89
  msgstr ""
90
 
91
- #: blogger-importer.php:284
92
- msgid "Blog Name"
93
  msgstr ""
94
 
95
- #: blogger-importer.php:285
96
- msgid "Blog URL"
97
  msgstr ""
98
 
99
- #: blogger-importer.php:286
100
- msgid "The Magic Button"
101
  msgstr ""
102
 
103
- #: blogger-importer.php:287
104
- msgid "Posts"
105
  msgstr ""
106
 
107
- #: blogger-importer.php:288
108
- msgid "Comments"
109
  msgstr ""
110
 
111
- #: blogger-importer.php:289
112
- msgid ""
113
- "This feature requires Javascript but it seems to be disabled. Please enable "
114
- "Javascript and then reload this page. Don&#8217;t worry, you can turn it "
115
- "back off when you&#8217;re done."
116
  msgstr ""
117
 
118
- #: blogger-importer.php:858
119
- msgid ""
120
- "All posts were imported with the current user as author. Use this form to "
121
- "move each Blogger user&#8217;s posts to a different WordPress user. You may "
122
- "<a href=\"%s\">add users</a> and then return to this page and complete the "
123
- "user mapping. This form may be used as many times as you like until you "
124
- "activate the &#8220;Restart&#8221; function below."
125
  msgstr ""
126
 
127
- #: blogger-importer.php:860
 
 
 
 
 
 
 
 
 
128
  msgid "Author mapping"
129
  msgstr ""
130
 
131
- #: blogger-importer.php:862
132
  msgid "Blogger username"
133
  msgstr ""
134
 
135
- #: blogger-importer.php:863
136
  msgid "WordPress login"
137
  msgstr ""
138
 
139
- #: blogger-importer.php:864
140
  msgid "Save Changes"
141
  msgstr ""
142
 
143
- #: blogger-importer.php:941
144
  msgid "Congratulations!"
145
  msgstr ""
146
 
147
- #: blogger-importer.php:941
148
- msgid ""
149
- "Now that you have imported your Blogger blog into WordPress, what are you "
150
- "going to do? Here are some suggestions:"
151
  msgstr ""
152
 
153
- #: blogger-importer.php:942
154
- msgid "That was hard work! Take a break."
155
  msgstr ""
156
 
157
- #: blogger-importer.php:944
158
- msgid ""
159
- "In case you haven&#8217;t done it already, you can import the posts from "
160
- "your other blogs:"
161
  msgstr ""
162
 
163
- #: blogger-importer.php:946
164
- msgid ""
165
- "Go to <a href=\"%s\" target=\"%s\">Authors &amp; Users</a>, where you can "
166
- "modify the new user(s) or delete them. If you want to make all of the "
167
- "imported posts yours, you will be given that option when you delete the new "
168
- "authors."
169
  msgstr ""
170
 
171
- #: blogger-importer.php:948
172
- msgid "For security, click the link below to reset this importer."
173
  msgstr ""
174
 
175
- #: blogger-importer.php:982
176
- msgid "Restart"
177
  msgstr ""
178
 
179
- #: blogger-importer.php:983
180
- msgid ""
181
- "We have saved some information about your Blogger account in your WordPress "
182
- "database. Clearing this information will allow you to start over. Restarting "
183
- "will not affect any posts you have already imported. If you attempt to re-"
184
- "import a blog, duplicate posts and comments will be skipped."
185
  msgstr ""
186
 
187
- #: blogger-importer.php:985
188
- msgid "Clear account information"
189
  msgstr ""
190
 
191
- #: blogger-importer.php:1026
192
- msgid "Blogger"
193
  msgstr ""
194
 
195
- #: blogger-importer.php:1026
196
- msgid ""
197
- "Import categories, posts and comments then maps users from a Blogger blog."
198
  msgstr ""
199
 
200
- #. Plugin Name of the plugin/theme
201
- msgid "Blogger Importer"
202
  msgstr ""
203
 
204
- #. Plugin URI of the plugin/theme
205
- msgid "http://wordpress.org/extend/plugins/blogger-importer/"
206
  msgstr ""
207
 
208
- #. Description of the plugin/theme
209
- msgid ""
210
- "Import posts, comments and tags from a Blogger blog and migrate authors to "
211
- "Wordpress users."
212
  msgstr ""
213
 
214
- #. Author of the plugin/theme
215
- msgid "wordpressdotorg"
216
  msgstr ""
217
 
218
- #. Author URI of the plugin/theme
219
- msgid "http://wordpress.org/"
220
  msgstr ""
 
2
  # This file is distributed under the same license as the Blogger Importer package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Blogger Importer 0.7\n"
6
+ "Report-Msgid-Bugs-To: \n"
7
+ "POT-Creation-Date: 2013-10-29 12:10-0000\n"
8
+ "PO-Revision-Date: 2013-10-29 12:11-0000\n"
9
+ "Last-Translator: Andy from Workshopshed <andy@workshopshed.com>\n"
10
+ "Language-Team: \n"
11
  "MIME-Version: 1.0\n"
12
  "Content-Type: text/plain; charset=UTF-8\n"
13
  "Content-Transfer-Encoding: 8bit\n"
14
+ "X-Poedit-Basepath: ./\n"
15
+ "X-Poedit-KeywordsList: _e;__\n"
16
+ "X-Poedit-SearchPath-0: ..\n"
17
 
18
+ #: ../blogger-importer-blog.php:462
19
+ #: ../blogger-importer-blog.php:787
20
+ msgid "Attempting to write back empty content"
21
  msgstr ""
22
 
23
+ #: ../blogger-importer-blog.php:502
24
+ #: ../blogger-importer-blog.php:505
25
+ #: ../blogger-importer-table.php:55
26
+ #: ../blogger-importer-table.php:59
27
+ #, php-format
28
+ msgid "%d of %d"
29
  msgstr ""
30
 
31
+ #: ../blogger-importer-blog.php:503
32
+ #, php-format
33
+ msgid "%d posts skipped"
 
34
  msgstr ""
35
 
36
+ #: ../blogger-importer-blog.php:506
37
+ #, php-format
38
+ msgid "%d comments skipped"
 
39
  msgstr ""
40
 
41
+ #: ../blogger-importer-blog.php:508
42
+ #, php-format
43
+ msgid "%d images skipped"
44
  msgstr ""
45
 
46
+ #: ../blogger-importer-blog.php:695
47
+ msgid "Lowrez not an image"
48
  msgstr ""
49
 
50
+ #: ../blogger-importer-blog.php:731
51
+ msgid "None of the images are valid"
52
  msgstr ""
53
 
54
+ #: ../blogger-importer-table.php:19
55
+ msgid "Blog Name"
56
  msgstr ""
57
 
58
+ #: ../blogger-importer-table.php:20
59
+ msgid "Blog URL"
 
 
60
  msgstr ""
61
 
62
+ #: ../blogger-importer-table.php:21
63
+ msgid "Posts"
64
+ msgstr ""
65
+
66
+ #: ../blogger-importer-table.php:22
67
+ msgid "Comments"
68
+ msgstr ""
69
+
70
+ #: ../blogger-importer-table.php:23
71
+ msgid "Images"
72
+ msgstr ""
73
+
74
+ #: ../blogger-importer-table.php:24
75
+ msgid "Links"
76
+ msgstr ""
77
+
78
+ #: ../blogger-importer-table.php:25
79
+ msgid "Action Button"
80
+ msgstr ""
81
+
82
+ #: ../blogger-importer-table.php:71
83
  msgid "Import"
84
  msgstr ""
85
 
86
+ #: ../blogger-importer-table.php:72
87
+ #: ../blogger-importer.php:84
88
  msgid "Continue"
89
  msgstr ""
90
 
91
+ #: ../blogger-importer-table.php:73
92
+ #: ../blogger-importer.php:84
93
  msgid "Importing..."
94
  msgstr ""
95
 
96
+ #: ../blogger-importer-table.php:74
97
+ #: ../blogger-importer.php:85
98
  msgid "Set Authors"
99
  msgstr ""
100
 
101
+ #: ../blogger-importer.php:77
102
+ msgid "Blogger"
103
  msgstr ""
104
 
105
+ #: ../blogger-importer.php:77
106
+ msgid "Import categories, posts, images and comments then maps users from a Blogger blog."
107
  msgstr ""
108
 
109
+ #: ../blogger-importer.php:85
110
  msgid "Nothing was imported. Had you already imported this blog?"
111
  msgstr ""
112
 
113
+ #: ../blogger-importer.php:85
114
+ msgid "Preparing author mapping form..."
115
  msgstr ""
116
 
117
+ #: ../blogger-importer.php:86
118
+ msgid "Final Step: Author Mapping"
119
  msgstr ""
120
 
121
+ #: ../blogger-importer.php:99
122
+ msgid "Import Blogger"
123
  msgstr ""
124
 
125
+ #: ../blogger-importer.php:100
126
+ msgid "Howdy! This importer allows you to import posts and comments from your Blogger account into your WordPress site."
127
  msgstr ""
128
 
129
+ #: ../blogger-importer.php:101
130
+ msgid "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)."
131
  msgstr ""
132
 
133
+ #: ../blogger-importer.php:102
134
+ msgid "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."
135
  msgstr ""
136
 
137
+ #: ../blogger-importer.php:104
138
+ msgid "Error occurred getting OAuth tokens from Google"
 
 
 
139
  msgstr ""
140
 
141
+ #: ../blogger-importer.php:142
142
+ msgid "OpenSSL is not installed check your PHP.INI or ask your server provider to enable the OpenSSL module."
 
 
 
 
 
143
  msgstr ""
144
 
145
+ #: ../blogger-importer.php:175
146
+ msgid "Invalid Token: Check server date, firewall settings and transports (curl, streams and fsockopen)"
147
+ msgstr ""
148
+
149
+ #: ../blogger-importer.php:394
150
+ #, php-format
151
+ msgid "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."
152
+ msgstr ""
153
+
154
+ #: ../blogger-importer.php:396
155
  msgid "Author mapping"
156
  msgstr ""
157
 
158
+ #: ../blogger-importer.php:398
159
  msgid "Blogger username"
160
  msgstr ""
161
 
162
+ #: ../blogger-importer.php:399
163
  msgid "WordPress login"
164
  msgstr ""
165
 
166
+ #: ../blogger-importer.php:400
167
  msgid "Save Changes"
168
  msgstr ""
169
 
170
+ #: ../blogger-importer.php:447
171
  msgid "Congratulations!"
172
  msgstr ""
173
 
174
+ #: ../blogger-importer.php:447
175
+ msgid "Now that you have imported your Blogger blog into WordPress, what are you going to do? Here are some suggestions:"
 
 
176
  msgstr ""
177
 
178
+ #: ../blogger-importer.php:451
179
+ msgid "For security, click the link below to reset this importer."
180
  msgstr ""
181
 
182
+ #: ../blogger-importer.php:460
183
+ msgid "Review posts"
 
 
184
  msgstr ""
185
 
186
+ #: ../blogger-importer.php:461
187
+ msgid "Review categories"
 
 
 
 
188
  msgstr ""
189
 
190
+ #: ../blogger-importer.php:462
191
+ msgid "Convert categories to tags"
192
  msgstr ""
193
 
194
+ #: ../blogger-importer.php:463
195
+ msgid "Review comments"
196
  msgstr ""
197
 
198
+ #: ../blogger-importer.php:464
199
+ msgid "Review media"
 
 
 
 
200
  msgstr ""
201
 
202
+ #: ../blogger-importer.php:468
203
+ msgid "In case you haven&#8217;t done it already, you can import the posts from your other blogs:"
204
  msgstr ""
205
 
206
+ #: ../blogger-importer.php:469
207
+ msgid "Show blogs"
208
  msgstr ""
209
 
210
+ #: ../blogger-importer.php:554
211
+ msgid "Trouble signing in"
 
212
  msgstr ""
213
 
214
+ #: ../blogger-importer.php:554
215
+ msgid "We were not able to gain access to your account. Try starting over."
216
  msgstr ""
217
 
218
+ #: ../blogger-importer.php:557
219
+ msgid "No blogs found"
220
  msgstr ""
221
 
222
+ #: ../blogger-importer.php:557
223
+ msgid "We were able to log in but there were no blogs. Try a different account next time."
 
 
224
  msgstr ""
225
 
226
+ #: ../blogger-importer.php:568
227
+ msgid "Restart"
228
  msgstr ""
229
 
230
+ #: ../blogger-importer.php:569
231
+ msgid "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."
232
  msgstr ""
233
+
oauth.php CHANGED
@@ -1,926 +1,930 @@
1
- <?php
2
- /*
3
- Original File from: http://oauth.googlecode.com/svn/code/php/
4
- License: MIT License:
5
-
6
- The MIT License
7
-
8
- Copyright (c) 2007 Andy Smith
9
-
10
- Permission is hereby granted, free of charge, to any person obtaining a copy
11
- of this software and associated documentation files (the "Software"), to deal
12
- in the Software without restriction, including without limitation the rights
13
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
- copies of the Software, and to permit persons to whom the Software is
15
- furnished to do so, subject to the following conditions:
16
-
17
- The above copyright notice and this permission notice shall be included in
18
- all copies or substantial portions of the Software.
19
-
20
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
- THE SOFTWARE.
27
-
28
-
29
- Modified for use in WordPress by Otto
30
-
31
- Changes: All classes renamed to Blogger_* to prevent conflicts with other plugins/code using the same OAuth library.
32
-
33
- */
34
- // vim: foldmethod=marker
35
-
36
- /* Generic exception class
37
- */
38
- class Blogger_OAuthException extends Exception {
39
- // pass
40
- }
41
-
42
- class Blogger_OAuthConsumer {
43
- public $key;
44
- public $secret;
45
-
46
- function __construct($key, $secret, $callback_url=NULL) {
47
- $this->key = $key;
48
- $this->secret = $secret;
49
- $this->callback_url = $callback_url;
50
- }
51
-
52
- function __toString() {
53
- return "Blogger_OAuthConsumer[key=$this->key,secret=$this->secret]";
54
- }
55
- }
56
-
57
- class Blogger_OAuthToken {
58
- // access tokens and request tokens
59
- public $key;
60
- public $secret;
61
-
62
- /**
63
- * key = the token
64
- * secret = the token secret
65
- */
66
- function __construct($key, $secret) {
67
- $this->key = $key;
68
- $this->secret = $secret;
69
- }
70
-
71
- /**
72
- * generates the basic string serialization of a token that a server
73
- * would respond to request_token and access_token calls with
74
- */
75
- function to_string() {
76
- return "oauth_token=" .
77
- Blogger_OAuthUtil::urlencode_rfc3986($this->key) .
78
- "&oauth_token_secret=" .
79
- Blogger_OAuthUtil::urlencode_rfc3986($this->secret);
80
- }
81
-
82
- function __toString() {
83
- return $this->to_string();
84
- }
85
- }
86
-
87
- /**
88
- * A class for implementing a Signature Method
89
- * See section 9 ("Signing Requests") in the spec
90
- */
91
- abstract class Blogger_OAuthSignatureMethod {
92
- /**
93
- * Needs to return the name of the Signature Method (ie HMAC-SHA1)
94
- * @return string
95
- */
96
- abstract public function get_name();
97
-
98
- /**
99
- * Build up the signature
100
- * NOTE: The output of this function MUST NOT be urlencoded.
101
- * the encoding is handled in Blogger_OAuthRequest when the final
102
- * request is serialized
103
- * @param Blogger_OAuthRequest $request
104
- * @param Blogger_OAuthConsumer $consumer
105
- * @param Blogger_OAuthToken $token
106
- * @return string
107
- */
108
- abstract public function build_signature($request, $consumer, $token);
109
-
110
- /**
111
- * Verifies that a given signature is correct
112
- * @param Blogger_OAuthRequest $request
113
- * @param Blogger_OAuthConsumer $consumer
114
- * @param Blogger_OAuthToken $token
115
- * @param string $signature
116
- * @return bool
117
- */
118
- public function check_signature($request, $consumer, $token, $signature) {
119
- $built = $this->build_signature($request, $consumer, $token);
120
-
121
- // Check for zero length, although unlikely here
122
- if (strlen($built) == 0 || strlen($signature) == 0) {
123
- return false;
124
- }
125
-
126
- if (strlen($built) != strlen($signature)) {
127
- return false;
128
- }
129
-
130
- // Avoid a timing leak with a (hopefully) time insensitive compare
131
- $result = 0;
132
- for ($i = 0; $i < strlen($signature); $i++) {
133
- $result |= ord($built{$i}) ^ ord($signature{$i});
134
- }
135
-
136
- return $result == 0;
137
- }
138
- }
139
-
140
- /**
141
- * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
142
- * where the Signature Base String is the text and the key is the concatenated values (each first
143
- * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
144
- * character (ASCII code 38) even if empty.
145
- * - Chapter 9.2 ("HMAC-SHA1")
146
- */
147
- class Blogger_OAuthSignatureMethod_HMAC_SHA1 extends Blogger_OAuthSignatureMethod {
148
- function get_name() {
149
- return "HMAC-SHA1";
150
- }
151
-
152
- public function build_signature($request, $consumer, $token) {
153
- $base_string = $request->get_signature_base_string();
154
- $request->base_string = $base_string;
155
-
156
- $key_parts = array(
157
- $consumer->secret,
158
- ($token) ? $token->secret : ""
159
- );
160
-
161
- $key_parts = Blogger_OAuthUtil::urlencode_rfc3986($key_parts);
162
- $key = implode('&', $key_parts);
163
-
164
- return base64_encode(hash_hmac('sha1', $base_string, $key, true));
165
- }
166
- }
167
-
168
- /**
169
- * The PLAINTEXT method does not provide any security protection and SHOULD only be used
170
- * over a secure channel such as HTTPS. It does not use the Signature Base String.
171
- * - Chapter 9.4 ("PLAINTEXT")
172
- */
173
- class Blogger_OAuthSignatureMethod_PLAINTEXT extends Blogger_OAuthSignatureMethod {
174
- public function get_name() {
175
- return "PLAINTEXT";
176
- }
177
-
178
- /**
179
- * oauth_signature is set to the concatenated encoded values of the Consumer Secret and
180
- * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
181
- * empty. The result MUST be encoded again.
182
- * - Chapter 9.4.1 ("Generating Signatures")
183
- *
184
- * Please note that the second encoding MUST NOT happen in the SignatureMethod, as
185
- * Blogger_OAuthRequest handles this!
186
- */
187
- public function build_signature($request, $consumer, $token) {
188
- $key_parts = array(
189
- $consumer->secret,
190
- ($token) ? $token->secret : ""
191
- );
192
-
193
- $key_parts = Blogger_OAuthUtil::urlencode_rfc3986($key_parts);
194
- $key = implode('&', $key_parts);
195
- $request->base_string = $key;
196
-
197
- return $key;
198
- }
199
- }
200
-
201
- /**
202
- * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
203
- * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
204
- * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
205
- * verified way to the Service Provider, in a manner which is beyond the scope of this
206
- * specification.
207
- * - Chapter 9.3 ("RSA-SHA1")
208
- */
209
- abstract class Blogger_OAuthSignatureMethod_RSA_SHA1 extends Blogger_OAuthSignatureMethod {
210
- public function get_name() {
211
- return "RSA-SHA1";
212
- }
213
-
214
- // Up to the SP to implement this lookup of keys. Possible ideas are:
215
- // (1) do a lookup in a table of trusted certs keyed off of consumer
216
- // (2) fetch via http using a url provided by the requester
217
- // (3) some sort of specific discovery code based on request
218
- //
219
- // Either way should return a string representation of the certificate
220
- protected abstract function fetch_public_cert(&$request);
221
-
222
- // Up to the SP to implement this lookup of keys. Possible ideas are:
223
- // (1) do a lookup in a table of trusted certs keyed off of consumer
224
- //
225
- // Either way should return a string representation of the certificate
226
- protected abstract function fetch_private_cert(&$request);
227
-
228
- public function build_signature($request, $consumer, $token) {
229
- $base_string = $request->get_signature_base_string();
230
- $request->base_string = $base_string;
231
-
232
- // Fetch the private key cert based on the request
233
- $cert = $this->fetch_private_cert($request);
234
-
235
- // Pull the private key ID from the certificate
236
- $privatekeyid = openssl_get_privatekey($cert);
237
-
238
- // Sign using the key
239
- $ok = openssl_sign($base_string, $signature, $privatekeyid);
240
-
241
- // Release the key resource
242
- openssl_free_key($privatekeyid);
243
-
244
- return base64_encode($signature);
245
- }
246
-
247
- public function check_signature($request, $consumer, $token, $signature) {
248
- $decoded_sig = base64_decode($signature);
249
-
250
- $base_string = $request->get_signature_base_string();
251
-
252
- // Fetch the public key cert based on the request
253
- $cert = $this->fetch_public_cert($request);
254
-
255
- // Pull the public key ID from the certificate
256
- $publickeyid = openssl_get_publickey($cert);
257
-
258
- // Check the computed signature against the one passed in the query
259
- $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
260
-
261
- // Release the key resource
262
- openssl_free_key($publickeyid);
263
-
264
- return $ok == 1;
265
- }
266
- }
267
-
268
- class Blogger_OAuthRequest {
269
- protected $parameters;
270
- protected $http_method;
271
- protected $http_url;
272
- // for debug purposes
273
- public $base_string;
274
- public static $version = '1.0';
275
- public static $POST_INPUT = 'php://input';
276
-
277
- function __construct($http_method, $http_url, $parameters=NULL) {
278
- $parameters = ($parameters) ? $parameters : array();
279
- $parameters = array_merge( Blogger_OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
280
- $this->parameters = $parameters;
281
- $this->http_method = $http_method;
282
- $this->http_url = $http_url;
283
- }
284
-
285
-
286
- /**
287
- * attempt to build up a request from what was passed to the server
288
- */
289
- public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {
290
- $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
291
- ? 'http'
292
- : 'https';
293
- $http_url = ($http_url) ? $http_url : $scheme .
294
- '://' . $_SERVER['SERVER_NAME'] .
295
- ':' .
296
- $_SERVER['SERVER_PORT'] .
297
- $_SERVER['REQUEST_URI'];
298
- $http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD'];
299
-
300
- // We weren't handed any parameters, so let's find the ones relevant to
301
- // this request.
302
- // If you run XML-RPC or similar you should use this to provide your own
303
- // parsed parameter-list
304
- if (!$parameters) {
305
- // Find request headers
306
- $request_headers = Blogger_OAuthUtil::get_headers();
307
-
308
- // Parse the query-string to find GET parameters
309
- $parameters = Blogger_OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
310
-
311
- // It's a POST request of the proper content-type, so parse POST
312
- // parameters and add those overriding any duplicates from GET
313
- if ($http_method == "POST"
314
- && isset($request_headers['Content-Type'])
315
- && strstr($request_headers['Content-Type'],
316
- 'application/x-www-form-urlencoded')
317
- ) {
318
- $post_data = Blogger_OAuthUtil::parse_parameters(
319
- file_get_contents(self::$POST_INPUT)
320
- );
321
- $parameters = array_merge($parameters, $post_data);
322
- }
323
-
324
- // We have a Authorization-header with Blogger_OAuth data. Parse the header
325
- // and add those overriding any duplicates from GET or POST
326
- if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'Blogger_OAuth ') {
327
- $header_parameters = Blogger_OAuthUtil::split_header(
328
- $request_headers['Authorization']
329
- );
330
- $parameters = array_merge($parameters, $header_parameters);
331
- }
332
-
333
- }
334
-
335
- return new Blogger_OAuthRequest($http_method, $http_url, $parameters);
336
- }
337
-
338
- /**
339
- * pretty much a helper function to set up the request
340
- */
341
- public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {
342
- $parameters = ($parameters) ? $parameters : array();
343
- $defaults = array("oauth_version" => Blogger_OAuthRequest::$version,
344
- "oauth_nonce" => Blogger_OAuthRequest::generate_nonce(),
345
- "oauth_timestamp" => Blogger_OAuthRequest::generate_timestamp(),
346
- "oauth_consumer_key" => $consumer->key);
347
- if ($token)
348
- $defaults['oauth_token'] = $token->key;
349
-
350
- $parameters = array_merge($defaults, $parameters);
351
-
352
- return new Blogger_OAuthRequest($http_method, $http_url, $parameters);
353
- }
354
-
355
- public function set_parameter($name, $value, $allow_duplicates = true) {
356
- if ($allow_duplicates && isset($this->parameters[$name])) {
357
- // We have already added parameter(s) with this name, so add to the list
358
- if (is_scalar($this->parameters[$name])) {
359
- // This is the first duplicate, so transform scalar (string)
360
- // into an array so we can add the duplicates
361
- $this->parameters[$name] = array($this->parameters[$name]);
362
- }
363
-
364
- $this->parameters[$name][] = $value;
365
- } else {
366
- $this->parameters[$name] = $value;
367
- }
368
- }
369
-
370
- public function get_parameter($name) {
371
- return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
372
- }
373
-
374
- public function get_parameters() {
375
- return $this->parameters;
376
- }
377
-
378
- public function unset_parameter($name) {
379
- unset($this->parameters[$name]);
380
- }
381
-
382
- /**
383
- * The request parameters, sorted and concatenated into a normalized string.
384
- * @return string
385
- */
386
- public function get_signable_parameters() {
387
- // Grab all parameters
388
- $params = $this->parameters;
389
-
390
- // Remove oauth_signature if present
391
- // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
392
- if (isset($params['oauth_signature'])) {
393
- unset($params['oauth_signature']);
394
- }
395
-
396
- return Blogger_OAuthUtil::build_http_query($params);
397
- }
398
-
399
- /**
400
- * Returns the base string of this request
401
- *
402
- * The base string defined as the method, the url
403
- * and the parameters (normalized), each urlencoded
404
- * and the concated with &.
405
- */
406
- public function get_signature_base_string() {
407
- $parts = array(
408
- $this->get_normalized_http_method(),
409
- $this->get_normalized_http_url(),
410
- $this->get_signable_parameters()
411
- );
412
-
413
- $parts = Blogger_OAuthUtil::urlencode_rfc3986($parts);
414
-
415
- return implode('&', $parts);
416
- }
417
-
418
- /**
419
- * just uppercases the http method
420
- */
421
- public function get_normalized_http_method() {
422
- return strtoupper($this->http_method);
423
- }
424
-
425
- /**
426
- * parses the url and rebuilds it to be
427
- * scheme://host/path
428
- */
429
- public function get_normalized_http_url() {
430
- $parts = parse_url($this->http_url);
431
-
432
- $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
433
- $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80');
434
- $host = (isset($parts['host'])) ? strtolower($parts['host']) : '';
435
- $path = (isset($parts['path'])) ? $parts['path'] : '';
436
-
437
- if (($scheme == 'https' && $port != '443')
438
- || ($scheme == 'http' && $port != '80')) {
439
- $host = "$host:$port";
440
- }
441
- return "$scheme://$host$path";
442
- }
443
-
444
- /**
445
- * builds a url usable for a GET request
446
- */
447
- public function to_url() {
448
- $post_data = $this->to_postdata();
449
- $out = $this->get_normalized_http_url();
450
- if ($post_data) {
451
- $out .= '?'.$post_data;
452
- }
453
- return $out;
454
- }
455
-
456
- /**
457
- * builds the data one would send in a POST request
458
- */
459
- public function to_postdata() {
460
- return Blogger_OAuthUtil::build_http_query($this->parameters);
461
- }
462
-
463
- /**
464
- * builds the Authorization: header
465
- */
466
- public function to_header($realm=null) {
467
- $first = true;
468
- if($realm) {
469
- $out = 'Authorization: Blogger_OAuth realm="' . Blogger_OAuthUtil::urlencode_rfc3986($realm) . '"';
470
- $first = false;
471
- } else
472
- $out = 'Authorization: Blogger_OAuth';
473
-
474
- $total = array();
475
- foreach ($this->parameters as $k => $v) {
476
- if (substr($k, 0, 5) != "oauth") continue;
477
- if (is_array($v)) {
478
- throw new Blogger_OAuthException('Arrays not supported in headers');
479
- }
480
- $out .= ($first) ? ' ' : ',';
481
- $out .= Blogger_OAuthUtil::urlencode_rfc3986($k) .
482
- '="' .
483
- Blogger_OAuthUtil::urlencode_rfc3986($v) .
484
- '"';
485
- $first = false;
486
- }
487
- return $out;
488
- }
489
-
490
- public function __toString() {
491
- return $this->to_url();
492
- }
493
-
494
-
495
- public function sign_request($signature_method, $consumer, $token) {
496
- $this->set_parameter(
497
- "oauth_signature_method",
498
- $signature_method->get_name(),
499
- false
500
- );
501
- $signature = $this->build_signature($signature_method, $consumer, $token);
502
- $this->set_parameter("oauth_signature", $signature, false);
503
- }
504
-
505
- public function build_signature($signature_method, $consumer, $token) {
506
- $signature = $signature_method->build_signature($this, $consumer, $token);
507
- return $signature;
508
- }
509
-
510
- /**
511
- * util function: current timestamp
512
- */
513
- private static function generate_timestamp() {
514
- return time();
515
- }
516
-
517
- /**
518
- * util function: current nonce
519
- */
520
- private static function generate_nonce() {
521
- $mt = microtime();
522
- $rand = mt_rand();
523
-
524
- return md5($mt . $rand); // md5s look nicer than numbers
525
- }
526
- }
527
-
528
- class Blogger_OAuthServer {
529
- protected $timestamp_threshold = 300; // in seconds, five minutes
530
- protected $version = '1.0'; // hi blaine
531
- protected $signature_methods = array();
532
-
533
- protected $data_store;
534
-
535
- function __construct($data_store) {
536
- $this->data_store = $data_store;
537
- }
538
-
539
- public function add_signature_method($signature_method) {
540
- $this->signature_methods[$signature_method->get_name()] =
541
- $signature_method;
542
- }
543
-
544
- // high level functions
545
-
546
- /**
547
- * process a request_token request
548
- * returns the request token on success
549
- */
550
- public function fetch_request_token(&$request) {
551
- $this->get_version($request);
552
-
553
- $consumer = $this->get_consumer($request);
554
-
555
- // no token required for the initial token request
556
- $token = NULL;
557
-
558
- $this->check_signature($request, $consumer, $token);
559
-
560
- // Rev A change
561
- $callback = $request->get_parameter('oauth_callback');
562
- $new_token = $this->data_store->new_request_token($consumer, $callback);
563
-
564
- return $new_token;
565
- }
566
-
567
- /**
568
- * process an access_token request
569
- * returns the access token on success
570
- */
571
- public function fetch_access_token(&$request) {
572
- $this->get_version($request);
573
-
574
- $consumer = $this->get_consumer($request);
575
-
576
- // requires authorized request token
577
- $token = $this->get_token($request, $consumer, "request");
578
-
579
- $this->check_signature($request, $consumer, $token);
580
-
581
- // Rev A change
582
- $verifier = $request->get_parameter('oauth_verifier');
583
- $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
584
-
585
- return $new_token;
586
- }
587
-
588
- /**
589
- * verify an api call, checks all the parameters
590
- */
591
- public function verify_request(&$request) {
592
- $this->get_version($request);
593
- $consumer = $this->get_consumer($request);
594
- $token = $this->get_token($request, $consumer, "access");
595
- $this->check_signature($request, $consumer, $token);
596
- return array($consumer, $token);
597
- }
598
-
599
- // Internals from here
600
- /**
601
- * version 1
602
- */
603
- private function get_version(&$request) {
604
- $version = $request->get_parameter("oauth_version");
605
- if (!$version) {
606
- // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
607
- // Chapter 7.0 ("Accessing Protected Ressources")
608
- $version = '1.0';
609
- }
610
- if ($version !== $this->version) {
611
- throw new Blogger_OAuthException("Blogger_OAuth version '$version' not supported");
612
- }
613
- return $version;
614
- }
615
-
616
- /**
617
- * figure out the signature with some defaults
618
- */
619
- private function get_signature_method($request) {
620
- $signature_method = $request instanceof Blogger_OAuthRequest
621
- ? $request->get_parameter("oauth_signature_method")
622
- : NULL;
623
-
624
- if (!$signature_method) {
625
- // According to chapter 7 ("Accessing Protected Ressources") the signature-method
626
- // parameter is required, and we can't just fallback to PLAINTEXT
627
- throw new Blogger_OAuthException('No signature method parameter. This parameter is required');
628
- }
629
-
630
- if (!in_array($signature_method,
631
- array_keys($this->signature_methods))) {
632
- throw new Blogger_OAuthException(
633
- "Signature method '$signature_method' not supported " .
634
- "try one of the following: " .
635
- implode(", ", array_keys($this->signature_methods))
636
- );
637
- }
638
- return $this->signature_methods[$signature_method];
639
- }
640
-
641
- /**
642
- * try to find the consumer for the provided request's consumer key
643
- */
644
- private function get_consumer($request) {
645
- $consumer_key = $request instanceof Blogger_OAuthRequest
646
- ? $request->get_parameter("oauth_consumer_key")
647
- : NULL;
648
-
649
- if (!$consumer_key) {
650
- throw new Blogger_OAuthException("Invalid consumer key");
651
- }
652
-
653
- $consumer = $this->data_store->lookup_consumer($consumer_key);
654
- if (!$consumer) {
655
- throw new Blogger_OAuthException("Invalid consumer");
656
- }
657
-
658
- return $consumer;
659
- }
660
-
661
- /**
662
- * try to find the token for the provided request's token key
663
- */
664
- private function get_token($request, $consumer, $token_type="access") {
665
- $token_field = $request instanceof Blogger_OAuthRequest
666
- ? $request->get_parameter('oauth_token')
667
- : NULL;
668
-
669
- $token = $this->data_store->lookup_token(
670
- $consumer, $token_type, $token_field
671
- );
672
- if (!$token) {
673
- throw new Blogger_OAuthException("Invalid $token_type token: $token_field");
674
- }
675
- return $token;
676
- }
677
-
678
- /**
679
- * all-in-one function to check the signature on a request
680
- * should guess the signature method appropriately
681
- */
682
- private function check_signature($request, $consumer, $token) {
683
- // this should probably be in a different method
684
- $timestamp = $request instanceof Blogger_OAuthRequest
685
- ? $request->get_parameter('oauth_timestamp')
686
- : NULL;
687
- $nonce = $request instanceof Blogger_OAuthRequest
688
- ? $request->get_parameter('oauth_nonce')
689
- : NULL;
690
-
691
- $this->check_timestamp($timestamp);
692
- $this->check_nonce($consumer, $token, $nonce, $timestamp);
693
-
694
- $signature_method = $this->get_signature_method($request);
695
-
696
- $signature = $request->get_parameter('oauth_signature');
697
- $valid_sig = $signature_method->check_signature(
698
- $request,
699
- $consumer,
700
- $token,
701
- $signature
702
- );
703
-
704
- if (!$valid_sig) {
705
- throw new Blogger_OAuthException("Invalid signature");
706
- }
707
- }
708
-
709
- /**
710
- * check that the timestamp is new enough
711
- */
712
- private function check_timestamp($timestamp) {
713
- if( ! $timestamp )
714
- throw new Blogger_OAuthException(
715
- 'Missing timestamp parameter. The parameter is required'
716
- );
717
-
718
- // verify that timestamp is recentish
719
- $now = time();
720
- if (abs($now - $timestamp) > $this->timestamp_threshold) {
721
- throw new Blogger_OAuthException(
722
- "Expired timestamp, yours $timestamp, ours $now"
723
- );
724
- }
725
- }
726
-
727
- /**
728
- * check that the nonce is not repeated
729
- */
730
- private function check_nonce($consumer, $token, $nonce, $timestamp) {
731
- if( ! $nonce )
732
- throw new Blogger_OAuthException(
733
- 'Missing nonce parameter. The parameter is required'
734
- );
735
-
736
- // verify that the nonce is uniqueish
737
- $found = $this->data_store->lookup_nonce(
738
- $consumer,
739
- $token,
740
- $nonce,
741
- $timestamp
742
- );
743
- if ($found) {
744
- throw new Blogger_OAuthException("Nonce already used: $nonce");
745
- }
746
- }
747
-
748
- }
749
-
750
- class Blogger_OAuthDataStore {
751
- function lookup_consumer($consumer_key) {
752
- // implement me
753
- }
754
-
755
- function lookup_token($consumer, $token_type, $token) {
756
- // implement me
757
- }
758
-
759
- function lookup_nonce($consumer, $token, $nonce, $timestamp) {
760
- // implement me
761
- }
762
-
763
- function new_request_token($consumer, $callback = null) {
764
- // return a new token attached to this consumer
765
- }
766
-
767
- function new_access_token($token, $consumer, $verifier = null) {
768
- // return a new access token attached to this consumer
769
- // for the user associated with this token if the request token
770
- // is authorized
771
- // should also invalidate the request token
772
- }
773
-
774
- }
775
-
776
- class Blogger_OAuthUtil {
777
- public static function urlencode_rfc3986($input) {
778
- if (is_array($input)) {
779
- return array_map(array('Blogger_OAuthUtil', 'urlencode_rfc3986'), $input);
780
- } else if (is_scalar($input)) {
781
- return str_replace(
782
- '+',
783
- ' ',
784
- str_replace('%7E', '~', rawurlencode($input))
785
- );
786
- } else {
787
- return '';
788
- }
789
- }
790
-
791
-
792
- // This decode function isn't taking into consideration the above
793
- // modifications to the encoding process. However, this method doesn't
794
- // seem to be used anywhere so leaving it as is.
795
- public static function urldecode_rfc3986($string) {
796
- return urldecode($string);
797
- }
798
-
799
- // Utility function for turning the Authorization: header into
800
- // parameters, has to do some unescaping
801
- // Can filter out any non-oauth parameters if needed (default behaviour)
802
- // May 28th, 2010 - method updated to tjerk.meesters for a speed improvement.
803
- // see http://code.google.com/p/oauth/issues/detail?id=163
804
- public static function split_header($header, $only_allow_oauth_parameters = true) {
805
- $params = array();
806
- if (preg_match_all('/('.($only_allow_oauth_parameters ? 'oauth_' : '').'[a-z_-]*)=(:?"([^"]*)"|([^,]*))/', $header, $matches)) {
807
- foreach ($matches[1] as $i => $h) {
808
- $params[$h] = Blogger_OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]);
809
- }
810
- if (isset($params['realm'])) {
811
- unset($params['realm']);
812
- }
813
- }
814
- return $params;
815
- }
816
-
817
- // helper to try to sort out headers for people who aren't running apache
818
- public static function get_headers() {
819
- if (function_exists('apache_request_headers')) {
820
- // we need this to get the actual Authorization: header
821
- // because apache tends to tell us it doesn't exist
822
- $headers = apache_request_headers();
823
-
824
- // sanitize the output of apache_request_headers because
825
- // we always want the keys to be Cased-Like-This and arh()
826
- // returns the headers in the same case as they are in the
827
- // request
828
- $out = array();
829
- foreach ($headers AS $key => $value) {
830
- $key = str_replace(
831
- " ",
832
- "-",
833
- ucwords(strtolower(str_replace("-", " ", $key)))
834
- );
835
- $out[$key] = $value;
836
- }
837
- } else {
838
- // otherwise we don't have apache and are just going to have to hope
839
- // that $_SERVER actually contains what we need
840
- $out = array();
841
- if( isset($_SERVER['CONTENT_TYPE']) )
842
- $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
843
- if( isset($_ENV['CONTENT_TYPE']) )
844
- $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
845
-
846
- foreach ($_SERVER as $key => $value) {
847
- if (substr($key, 0, 5) == "HTTP_") {
848
- // this is chaos, basically it is just there to capitalize the first
849
- // letter of every word that is not an initial HTTP and strip HTTP
850
- // code from przemek
851
- $key = str_replace(
852
- " ",
853
- "-",
854
- ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
855
- );
856
- $out[$key] = $value;
857
- }
858
- }
859
- }
860
- return $out;
861
- }
862
-
863
- // This function takes a input like a=b&a=c&d=e and returns the parsed
864
- // parameters like this
865
- // array('a' => array('b','c'), 'd' => 'e')
866
- public static function parse_parameters( $input ) {
867
- if (!isset($input) || !$input) return array();
868
-
869
- $pairs = explode('&', $input);
870
-
871
- $parsed_parameters = array();
872
- foreach ($pairs as $pair) {
873
- $split = explode('=', $pair, 2);
874
- $parameter = Blogger_OAuthUtil::urldecode_rfc3986($split[0]);
875
- $value = isset($split[1]) ? Blogger_OAuthUtil::urldecode_rfc3986($split[1]) : '';
876
-
877
- if (isset($parsed_parameters[$parameter])) {
878
- // We have already recieved parameter(s) with this name, so add to the list
879
- // of parameters with this name
880
-
881
- if (is_scalar($parsed_parameters[$parameter])) {
882
- // This is the first duplicate, so transform scalar (string) into an array
883
- // so we can add the duplicates
884
- $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
885
- }
886
-
887
- $parsed_parameters[$parameter][] = $value;
888
- } else {
889
- $parsed_parameters[$parameter] = $value;
890
- }
891
- }
892
- return $parsed_parameters;
893
- }
894
-
895
- public static function build_http_query($params) {
896
- if (!$params) return '';
897
-
898
- // Urlencode both keys and values
899
- $keys = Blogger_OAuthUtil::urlencode_rfc3986(array_keys($params));
900
- $values = Blogger_OAuthUtil::urlencode_rfc3986(array_values($params));
901
- $params = array_combine($keys, $values);
902
-
903
- // Parameters are sorted by name, using lexicographical byte value ordering.
904
- // Ref: Spec: 9.1.1 (1)
905
- uksort($params, 'strcmp');
906
-
907
- $pairs = array();
908
- foreach ($params as $parameter => $value) {
909
- if (is_array($value)) {
910
- // If two or more parameters share the same name, they are sorted by their value
911
- // Ref: Spec: 9.1.1 (1)
912
- // June 12th, 2010 - changed to sort because of issue 164 by hidetaka
913
- sort($value, SORT_STRING);
914
- foreach ($value as $duplicate_value) {
915
- $pairs[] = $parameter . '=' . $duplicate_value;
916
- }
917
- } else {
918
- $pairs[] = $parameter . '=' . $value;
919
- }
920
- }
921
- // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
922
- // Each name-value pair is separated by an '&' character (ASCII code 38)
923
- return implode('&', $pairs);
924
- }
925
- }
926
-
 
 
 
 
1
+ <?php
2
+ /*
3
+ Original File from: http://oauth.googlecode.com/svn/code/php/
4
+ License: MIT License:
5
+
6
+ The MIT License
7
+
8
+ Copyright (c) 2007 Andy Smith
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in
18
+ all copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
+ THE SOFTWARE.
27
+
28
+
29
+ Modified for use in WordPress by Otto
30
+
31
+ Changes: All classes renamed to Blogger_* to prevent conflicts with other plugins/code using the same OAuth library.
32
+
33
+ */
34
+ // vim: foldmethod=marker
35
+
36
+ /* Generic exception class
37
+ */
38
+ class Blogger_OAuthException extends Exception {
39
+ // pass
40
+ }
41
+
42
+ class Blogger_OAuthConsumer {
43
+ public $key;
44
+ public $secret;
45
+
46
+ function __construct($key, $secret, $callback_url=NULL) {
47
+ $this->key = $key;
48
+ $this->secret = $secret;
49
+ $this->callback_url = $callback_url;
50
+ }
51
+
52
+ function __toString() {
53
+ return "Blogger_OAuthConsumer[key=$this->key,secret=$this->secret]";
54
+ }
55
+ }
56
+
57
+ class Blogger_OAuthToken {
58
+ // access tokens and request tokens
59
+ public $key;
60
+ public $secret;
61
+
62
+ /**
63
+ * key = the token
64
+ * secret = the token secret
65
+ */
66
+ function __construct($key, $secret) {
67
+ $this->key = $key;
68
+ $this->secret = $secret;
69
+ }
70
+
71
+ /**
72
+ * generates the basic string serialization of a token that a server
73
+ * would respond to request_token and access_token calls with
74
+ */
75
+ function to_string() {
76
+ return "oauth_token=" .
77
+ Blogger_OAuthUtil::urlencode_rfc3986($this->key) .
78
+ "&oauth_token_secret=" .
79
+ Blogger_OAuthUtil::urlencode_rfc3986($this->secret);
80
+ }
81
+
82
+ function __toString() {
83
+ return $this->to_string();
84
+ }
85
+ }
86
+
87
+ /**
88
+ * A class for implementing a Signature Method
89
+ * See section 9 ("Signing Requests") in the spec
90
+ */
91
+ abstract class Blogger_OAuthSignatureMethod {
92
+ /**
93
+ * Needs to return the name of the Signature Method (ie HMAC-SHA1)
94
+ * @return string
95
+ */
96
+ abstract public function get_name();
97
+
98
+ /**
99
+ * Build up the signature
100
+ * NOTE: The output of this function MUST NOT be urlencoded.
101
+ * the encoding is handled in Blogger_OAuthRequest when the final
102
+ * request is serialized
103
+ * @param Blogger_OAuthRequest $request
104
+ * @param Blogger_OAuthConsumer $consumer
105
+ * @param Blogger_OAuthToken $token
106
+ * @return string
107
+ */
108
+ abstract public function build_signature($request, $consumer, $token);
109
+
110
+ /**
111
+ * Verifies that a given signature is correct
112
+ * @param Blogger_OAuthRequest $request
113
+ * @param Blogger_OAuthConsumer $consumer
114
+ * @param Blogger_OAuthToken $token
115
+ * @param string $signature
116
+ * @return bool
117
+ */
118
+ public function check_signature($request, $consumer, $token, $signature) {
119
+ $built = $this->build_signature($request, $consumer, $token);
120
+
121
+ // Check for zero length, although unlikely here
122
+ if (strlen($built) == 0 || strlen($signature) == 0) {
123
+ return false;
124
+ }
125
+
126
+ if (strlen($built) != strlen($signature)) {
127
+ return false;
128
+ }
129
+
130
+ // Avoid a timing leak with a (hopefully) time insensitive compare
131
+ $result = 0;
132
+ for ($i = 0; $i < strlen($signature); $i++) {
133
+ $result |= ord($built{$i}) ^ ord($signature{$i});
134
+ }
135
+
136
+ return $result == 0;
137
+ }
138
+ }
139
+
140
+ /**
141
+ * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
142
+ * where the Signature Base String is the text and the key is the concatenated values (each first
143
+ * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
144
+ * character (ASCII code 38) even if empty.
145
+ * - Chapter 9.2 ("HMAC-SHA1")
146
+ */
147
+ class Blogger_OAuthSignatureMethod_HMAC_SHA1 extends Blogger_OAuthSignatureMethod {
148
+ function get_name() {
149
+ return "HMAC-SHA1";
150
+ }
151
+
152
+ public function build_signature($request, $consumer, $token) {
153
+ $base_string = $request->get_signature_base_string();
154
+ $request->base_string = $base_string;
155
+
156
+ $key_parts = array(
157
+ $consumer->secret,
158
+ ($token) ? $token->secret : ""
159
+ );
160
+
161
+ $key_parts = Blogger_OAuthUtil::urlencode_rfc3986($key_parts);
162
+ $key = implode('&', $key_parts);
163
+
164
+ return base64_encode(hash_hmac('sha1', $base_string, $key, true));
165
+ }
166
+ }
167
+
168
+ /**
169
+ * The PLAINTEXT method does not provide any security protection and SHOULD only be used
170
+ * over a secure channel such as HTTPS. It does not use the Signature Base String.
171
+ * - Chapter 9.4 ("PLAINTEXT")
172
+ */
173
+ class Blogger_OAuthSignatureMethod_PLAINTEXT extends Blogger_OAuthSignatureMethod {
174
+ public function get_name() {
175
+ return "PLAINTEXT";
176
+ }
177
+
178
+ /**
179
+ * oauth_signature is set to the concatenated encoded values of the Consumer Secret and
180
+ * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
181
+ * empty. The result MUST be encoded again.
182
+ * - Chapter 9.4.1 ("Generating Signatures")
183
+ *
184
+ * Please note that the second encoding MUST NOT happen in the SignatureMethod, as
185
+ * Blogger_OAuthRequest handles this!
186
+ */
187
+ public function build_signature($request, $consumer, $token) {
188
+ $key_parts = array(
189
+ $consumer->secret,
190
+ ($token) ? $token->secret : ""
191
+ );
192
+
193
+ $key_parts = Blogger_OAuthUtil::urlencode_rfc3986($key_parts);
194
+ $key = implode('&', $key_parts);
195
+ $request->base_string = $key;
196
+
197
+ return $key;
198
+ }
199
+ }
200
+
201
+ /**
202
+ * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
203
+ * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
204
+ * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
205
+ * verified way to the Service Provider, in a manner which is beyond the scope of this
206
+ * specification.
207
+ * - Chapter 9.3 ("RSA-SHA1")
208
+ */
209
+ abstract class Blogger_OAuthSignatureMethod_RSA_SHA1 extends Blogger_OAuthSignatureMethod {
210
+ public function get_name() {
211
+ return "RSA-SHA1";
212
+ }
213
+
214
+ // Up to the SP to implement this lookup of keys. Possible ideas are:
215
+ // (1) do a lookup in a table of trusted certs keyed off of consumer
216
+ // (2) fetch via http using a url provided by the requester
217
+ // (3) some sort of specific discovery code based on request
218
+ //
219
+ // Either way should return a string representation of the certificate
220
+ protected abstract function fetch_public_cert(&$request);
221
+
222
+ // Up to the SP to implement this lookup of keys. Possible ideas are:
223
+ // (1) do a lookup in a table of trusted certs keyed off of consumer
224
+ //
225
+ // Either way should return a string representation of the certificate
226
+ protected abstract function fetch_private_cert(&$request);
227
+
228
+ public function build_signature($request, $consumer, $token) {
229
+ $base_string = $request->get_signature_base_string();
230
+ $request->base_string = $base_string;
231
+
232
+ // Fetch the private key cert based on the request
233
+ $cert = $this->fetch_private_cert($request);
234
+
235
+ // Pull the private key ID from the certificate
236
+ $privatekeyid = openssl_get_privatekey($cert);
237
+
238
+ // Sign using the key
239
+ $ok = openssl_sign($base_string, $signature, $privatekeyid);
240
+
241
+ // Release the key resource
242
+ openssl_free_key($privatekeyid);
243
+
244
+ return base64_encode($signature);
245
+ }
246
+
247
+ public function check_signature($request, $consumer, $token, $signature) {
248
+ $decoded_sig = base64_decode($signature);
249
+
250
+ $base_string = $request->get_signature_base_string();
251
+
252
+ // Fetch the public key cert based on the request
253
+ $cert = $this->fetch_public_cert($request);
254
+
255
+ // Pull the public key ID from the certificate
256
+ $publickeyid = openssl_get_publickey($cert);
257
+
258
+ // Check the computed signature against the one passed in the query
259
+ $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
260
+
261
+ // Release the key resource
262
+ openssl_free_key($publickeyid);
263
+
264
+ return $ok == 1;
265
+ }
266
+ }
267
+
268
+ class Blogger_OAuthRequest {
269
+ protected $parameters;
270
+ protected $http_method;
271
+ protected $http_url;
272
+ // for debug purposes
273
+ public $base_string;
274
+ public static $version = '1.0';
275
+ public static $POST_INPUT = 'php://input';
276
+
277
+ function __construct($http_method, $http_url, $parameters=NULL) {
278
+ $parameters = ($parameters) ? $parameters : array();
279
+ $parameters = array_merge( Blogger_OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
280
+ $this->parameters = $parameters;
281
+ $this->http_method = $http_method;
282
+ $this->http_url = $http_url;
283
+ }
284
+
285
+
286
+ /**
287
+ * attempt to build up a request from what was passed to the server
288
+ */
289
+ public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {
290
+ $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
291
+ ? 'http'
292
+ : 'https';
293
+ $http_url = ($http_url) ? $http_url : $scheme .
294
+ '://' . $_SERVER['SERVER_NAME'] .
295
+ ':' .
296
+ $_SERVER['SERVER_PORT'] .
297
+ $_SERVER['REQUEST_URI'];
298
+ $http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD'];
299
+
300
+ // We weren't handed any parameters, so let's find the ones relevant to
301
+ // this request.
302
+ // If you run XML-RPC or similar you should use this to provide your own
303
+ // parsed parameter-list
304
+ if (!$parameters) {
305
+ // Find request headers
306
+ $request_headers = Blogger_OAuthUtil::get_headers();
307
+
308
+ // Parse the query-string to find GET parameters
309
+ $parameters = Blogger_OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
310
+
311
+ // It's a POST request of the proper content-type, so parse POST
312
+ // parameters and add those overriding any duplicates from GET
313
+ if ($http_method == "POST"
314
+ && isset($request_headers['Content-Type'])
315
+ && strstr($request_headers['Content-Type'],
316
+ 'application/x-www-form-urlencoded')
317
+ ) {
318
+ $post_data = Blogger_OAuthUtil::parse_parameters(
319
+ file_get_contents(self::$POST_INPUT)
320
+ );
321
+ $parameters = array_merge($parameters, $post_data);
322
+ }
323
+
324
+ // We have a Authorization-header with Blogger_OAuth data. Parse the header
325
+ // and add those overriding any duplicates from GET or POST
326
+ if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'Blogger_OAuth ') {
327
+ $header_parameters = Blogger_OAuthUtil::split_header(
328
+ $request_headers['Authorization']
329
+ );
330
+ $parameters = array_merge($parameters, $header_parameters);
331
+ }
332
+
333
+ }
334
+
335
+ return new Blogger_OAuthRequest($http_method, $http_url, $parameters);
336
+ }
337
+
338
+ /**
339
+ * pretty much a helper function to set up the request
340
+ */
341
+ public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {
342
+ $parameters = ($parameters) ? $parameters : array();
343
+ $defaults = array("oauth_version" => Blogger_OAuthRequest::$version,
344
+ "oauth_nonce" => Blogger_OAuthRequest::generate_nonce(),
345
+ "oauth_timestamp" => Blogger_OAuthRequest::generate_timestamp(),
346
+ "oauth_consumer_key" => $consumer->key);
347
+ if ($token)
348
+ $defaults['oauth_token'] = $token->key;
349
+
350
+ $parameters = array_merge($defaults, $parameters);
351
+
352
+ return new Blogger_OAuthRequest($http_method, $http_url, $parameters);
353
+ }
354
+
355
+ public function set_parameter($name, $value, $allow_duplicates = true) {
356
+ if ($allow_duplicates && isset($this->parameters[$name])) {
357
+ // We have already added parameter(s) with this name, so add to the list
358
+ if (is_scalar($this->parameters[$name])) {
359
+ // This is the first duplicate, so transform scalar (string)
360
+ // into an array so we can add the duplicates
361
+ $this->parameters[$name] = array($this->parameters[$name]);
362
+ }
363
+
364
+ $this->parameters[$name][] = $value;
365
+ } else {
366
+ $this->parameters[$name] = $value;
367
+ }
368
+ }
369
+
370
+ public function get_parameter($name) {
371
+ return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
372
+ }
373
+
374
+ public function get_parameters() {
375
+ return $this->parameters;
376
+ }
377
+
378
+ public function unset_parameter($name) {
379
+ unset($this->parameters[$name]);
380
+ }
381
+
382
+ /**
383
+ * The request parameters, sorted and concatenated into a normalized string.
384
+ * @return string
385
+ */
386
+ public function get_signable_parameters() {
387
+ // Grab all parameters
388
+ $params = $this->parameters;
389
+
390
+ // Remove oauth_signature if present
391
+ // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
392
+ if (isset($params['oauth_signature'])) {
393
+ unset($params['oauth_signature']);
394
+ }
395
+
396
+ return Blogger_OAuthUtil::build_http_query($params);
397
+ }
398
+
399
+ /**
400
+ * Returns the base string of this request
401
+ *
402
+ * The base string defined as the method, the url
403
+ * and the parameters (normalized), each urlencoded
404
+ * and the concated with &.
405
+ */
406
+ public function get_signature_base_string() {
407
+ $parts = array(
408
+ $this->get_normalized_http_method(),
409
+ $this->get_normalized_http_url(),
410
+ $this->get_signable_parameters()
411
+ );
412
+
413
+ $parts = Blogger_OAuthUtil::urlencode_rfc3986($parts);
414
+
415
+ return implode('&', $parts);
416
+ }
417
+
418
+ /**
419
+ * just uppercases the http method
420
+ */
421
+ public function get_normalized_http_method() {
422
+ return strtoupper($this->http_method);
423
+ }
424
+
425
+ /**
426
+ * parses the url and rebuilds it to be
427
+ * scheme://host/path
428
+ */
429
+ public function get_normalized_http_url() {
430
+ $parts = parse_url($this->http_url);
431
+
432
+ $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
433
+ $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80');
434
+ $host = (isset($parts['host'])) ? strtolower($parts['host']) : '';
435
+ $path = (isset($parts['path'])) ? $parts['path'] : '';
436
+
437
+ if (($scheme == 'https' && $port != '443')
438
+ || ($scheme == 'http' && $port != '80')) {
439
+ $host = "$host:$port";
440
+ }
441
+ return "$scheme://$host$path";
442
+ }
443
+
444
+ /**
445
+ * builds a url usable for a GET request
446
+ */
447
+ public function to_url() {
448
+ $post_data = $this->to_postdata();
449
+ $out = $this->get_normalized_http_url();
450
+ if ($post_data) {
451
+ $out .= '?'.$post_data;
452
+ }
453
+ return $out;
454
+ }
455
+
456
+ /**
457
+ * builds the data one would send in a POST request
458
+ */
459
+ public function to_postdata() {
460
+ return Blogger_OAuthUtil::build_http_query($this->parameters);
461
+ }
462
+
463
+ /**
464
+ * builds the Authorization: header
465
+ */
466
+ public function to_header($realm=null) {
467
+ $first = true;
468
+ if($realm) {
469
+ $out = 'Authorization: Blogger_OAuth realm="' . Blogger_OAuthUtil::urlencode_rfc3986($realm) . '"';
470
+ $first = false;
471
+ } else
472
+ $out = 'Authorization: Blogger_OAuth';
473
+
474
+ $total = array();
475
+ foreach ($this->parameters as $k => $v) {
476
+ if (substr($k, 0, 5) != "oauth") continue;
477
+ if (is_array($v)) {
478
+ throw new Blogger_OAuthException('Arrays not supported in headers');
479
+ }
480
+ $out .= ($first) ? ' ' : ',';
481
+ $out .= Blogger_OAuthUtil::urlencode_rfc3986($k) .
482
+ '="' .
483
+ Blogger_OAuthUtil::urlencode_rfc3986($v) .
484
+ '"';
485
+ $first = false;
486
+ }
487
+ return $out;
488
+ }
489
+
490
+ public function __toString() {
491
+ return $this->to_url();
492
+ }
493
+
494
+
495
+ public function sign_request($signature_method, $consumer, $token) {
496
+ $this->set_parameter(
497
+ "oauth_signature_method",
498
+ $signature_method->get_name(),
499
+ false
500
+ );
501
+ $signature = $this->build_signature($signature_method, $consumer, $token);
502
+ $this->set_parameter("oauth_signature", $signature, false);
503
+ }
504
+
505
+ public function build_signature($signature_method, $consumer, $token) {
506
+ $signature = $signature_method->build_signature($this, $consumer, $token);
507
+ return $signature;
508
+ }
509
+
510
+ /**
511
+ * util function: current timestamp
512
+ */
513
+ private static function generate_timestamp() {
514
+ return time();
515
+ }
516
+
517
+ /**
518
+ * util function: current nonce
519
+ */
520
+ private static function generate_nonce() {
521
+ $mt = microtime();
522
+ $rand = mt_rand();
523
+
524
+ return md5($mt . $rand); // md5s look nicer than numbers
525
+ }
526
+ }
527
+
528
+ class Blogger_OAuthServer {
529
+ protected $timestamp_threshold = 300; // in seconds, five minutes
530
+ protected $version = '1.0'; // hi blaine
531
+ protected $signature_methods = array();
532
+
533
+ protected $data_store;
534
+
535
+ function __construct($data_store) {
536
+ $this->data_store = $data_store;
537
+ }
538
+
539
+ public function add_signature_method($signature_method) {
540
+ $this->signature_methods[$signature_method->get_name()] =
541
+ $signature_method;
542
+ }
543
+
544
+ // high level functions
545
+
546
+ /**
547
+ * process a request_token request
548
+ * returns the request token on success
549
+ */
550
+ public function fetch_request_token(&$request) {
551
+ $this->get_version($request);
552
+
553
+ $consumer = $this->get_consumer($request);
554
+
555
+ // no token required for the initial token request
556
+ $token = NULL;
557
+
558
+ $this->check_signature($request, $consumer, $token);
559
+
560
+ // Rev A change
561
+ $callback = $request->get_parameter('oauth_callback');
562
+ $new_token = $this->data_store->new_request_token($consumer, $callback);
563
+
564
+ return $new_token;
565
+ }
566
+
567
+ /**
568
+ * process an access_token request
569
+ * returns the access token on success
570
+ */
571
+ public function fetch_access_token(&$request) {
572
+ $this->get_version($request);
573
+
574
+ $consumer = $this->get_consumer($request);
575
+
576
+ // requires authorized request token
577
+ $token = $this->get_token($request, $consumer, "request");
578
+
579
+ $this->check_signature($request, $consumer, $token);
580
+
581
+ // Rev A change
582
+ $verifier = $request->get_parameter('oauth_verifier');
583
+ $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
584
+
585
+ return $new_token;
586
+ }
587
+
588
+ /**
589
+ * verify an api call, checks all the parameters
590
+ */
591
+ public function verify_request(&$request) {
592
+ $this->get_version($request);
593
+ $consumer = $this->get_consumer($request);
594
+ $token = $this->get_token($request, $consumer, "access");
595
+ $this->check_signature($request, $consumer, $token);
596
+ return array($consumer, $token);
597
+ }
598
+
599
+ // Internals from here
600
+ /**
601
+ * version 1
602
+ */
603
+ private function get_version(&$request) {
604
+ $version = $request->get_parameter("oauth_version");
605
+ if (!$version) {
606
+ // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
607
+ // Chapter 7.0 ("Accessing Protected Ressources")
608
+ $version = '1.0';
609
+ }
610
+ if ($version !== $this->version) {
611
+ throw new Blogger_OAuthException("Blogger_OAuth version '$version' not supported");
612
+ }
613
+ return $version;
614
+ }
615
+
616
+ /**
617
+ * figure out the signature with some defaults
618
+ */
619
+ private function get_signature_method($request) {
620
+ $signature_method = $request instanceof Blogger_OAuthRequest
621
+ ? $request->get_parameter("oauth_signature_method")
622
+ : NULL;
623
+
624
+ if (!$signature_method) {
625
+ // According to chapter 7 ("Accessing Protected Ressources") the signature-method
626
+ // parameter is required, and we can't just fallback to PLAINTEXT
627
+ throw new Blogger_OAuthException('No signature method parameter. This parameter is required');
628
+ }
629
+
630
+ if (!in_array($signature_method,
631
+ array_keys($this->signature_methods))) {
632
+ throw new Blogger_OAuthException(
633
+ "Signature method '$signature_method' not supported " .
634
+ "try one of the following: " .
635
+ implode(", ", array_keys($this->signature_methods))
636
+ );
637
+ }
638
+ return $this->signature_methods[$signature_method];
639
+ }
640
+
641
+ /**
642
+ * try to find the consumer for the provided request's consumer key
643
+ */
644
+ private function get_consumer($request) {
645
+ $consumer_key = $request instanceof Blogger_OAuthRequest
646
+ ? $request->get_parameter("oauth_consumer_key")
647
+ : NULL;
648
+
649
+ if (!$consumer_key) {
650
+ throw new Blogger_OAuthException("Invalid consumer key");
651
+ }
652
+
653
+ $consumer = $this->data_store->lookup_consumer($consumer_key);
654
+ if (!$consumer) {
655
+ throw new Blogger_OAuthException("Invalid consumer");
656
+ }
657
+
658
+ return $consumer;
659
+ }
660
+
661
+ /**
662
+ * try to find the token for the provided request's token key
663
+ */
664
+ private function get_token($request, $consumer, $token_type="access") {
665
+ $token_field = $request instanceof Blogger_OAuthRequest
666
+ ? $request->get_parameter('oauth_token')
667
+ : NULL;
668
+
669
+ $token = $this->data_store->lookup_token(
670
+ $consumer, $token_type, $token_field
671
+ );
672
+ if (!$token) {
673
+ throw new Blogger_OAuthException("Invalid $token_type token: $token_field");
674
+ }
675
+ return $token;
676
+ }
677
+
678
+ /**
679
+ * all-in-one function to check the signature on a request
680
+ * should guess the signature method appropriately
681
+ */
682
+ private function check_signature($request, $consumer, $token) {
683
+ // this should probably be in a different method
684
+ $timestamp = $request instanceof Blogger_OAuthRequest
685
+ ? $request->get_parameter('oauth_timestamp')
686
+ : NULL;
687
+ $nonce = $request instanceof Blogger_OAuthRequest
688
+ ? $request->get_parameter('oauth_nonce')
689
+ : NULL;
690
+
691
+ $this->check_timestamp($timestamp);
692
+ $this->check_nonce($consumer, $token, $nonce, $timestamp);
693
+
694
+ $signature_method = $this->get_signature_method($request);
695
+
696
+ $signature = $request->get_parameter('oauth_signature');
697
+ $valid_sig = $signature_method->check_signature(
698
+ $request,
699
+ $consumer,
700
+ $token,
701
+ $signature
702
+ );
703
+
704
+ if (!$valid_sig) {
705
+ throw new Blogger_OAuthException("Invalid signature");
706
+ }
707
+ }
708
+
709
+ /**
710
+ * check that the timestamp is new enough
711
+ */
712
+ private function check_timestamp($timestamp) {
713
+ if( ! $timestamp )
714
+ throw new Blogger_OAuthException(
715
+ 'Missing timestamp parameter. The parameter is required'
716
+ );
717
+
718
+ // verify that timestamp is recentish
719
+ $now = time();
720
+ if (abs($now - $timestamp) > $this->timestamp_threshold) {
721
+ throw new Blogger_OAuthException(
722
+ "Expired timestamp, yours $timestamp, ours $now"
723
+ );
724
+ }
725
+ }
726
+
727
+ /**
728
+ * check that the nonce is not repeated
729
+ */
730
+ private function check_nonce($consumer, $token, $nonce, $timestamp) {
731
+ if( ! $nonce )
732
+ throw new Blogger_OAuthException(
733
+ 'Missing nonce parameter. The parameter is required'
734
+ );
735
+
736
+ // verify that the nonce is uniqueish
737
+ $found = $this->data_store->lookup_nonce(
738
+ $consumer,
739
+ $token,
740
+ $nonce,
741
+ $timestamp
742
+ );
743
+ if ($found) {
744
+ throw new Blogger_OAuthException("Nonce already used: $nonce");
745
+ }
746
+ }
747
+
748
+ }
749
+
750
+ class Blogger_OAuthDataStore {
751
+ function lookup_consumer($consumer_key) {
752
+ // implement me
753
+ }
754
+
755
+ function lookup_token($consumer, $token_type, $token) {
756
+ // implement me
757
+ }
758
+
759
+ function lookup_nonce($consumer, $token, $nonce, $timestamp) {
760
+ // implement me
761
+ }
762
+
763
+ function new_request_token($consumer, $callback = null) {
764
+ // return a new token attached to this consumer
765
+ }
766
+
767
+ function new_access_token($token, $consumer, $verifier = null) {
768
+ // return a new access token attached to this consumer
769
+ // for the user associated with this token if the request token
770
+ // is authorized
771
+ // should also invalidate the request token
772
+ }
773
+
774
+ }
775
+
776
+ class Blogger_OAuthUtil {
777
+ public static function sslinstalled() {
778
+ return function_exists('openssl_sign');
779
+ }
780
+
781
+ public static function urlencode_rfc3986($input) {
782
+ if (is_array($input)) {
783
+ return array_map(array('Blogger_OAuthUtil', 'urlencode_rfc3986'), $input);
784
+ } else if (is_scalar($input)) {
785
+ return str_replace(
786
+ '+',
787
+ ' ',
788
+ str_replace('%7E', '~', rawurlencode($input))
789
+ );
790
+ } else {
791
+ return '';
792
+ }
793
+ }
794
+
795
+
796
+ // This decode function isn't taking into consideration the above
797
+ // modifications to the encoding process. However, this method doesn't
798
+ // seem to be used anywhere so leaving it as is.
799
+ public static function urldecode_rfc3986($string) {
800
+ return urldecode($string);
801
+ }
802
+
803
+ // Utility function for turning the Authorization: header into
804
+ // parameters, has to do some unescaping
805
+ // Can filter out any non-oauth parameters if needed (default behaviour)
806
+ // May 28th, 2010 - method updated to tjerk.meesters for a speed improvement.
807
+ // see http://code.google.com/p/oauth/issues/detail?id=163
808
+ public static function split_header($header, $only_allow_oauth_parameters = true) {
809
+ $params = array();
810
+ if (preg_match_all('/('.($only_allow_oauth_parameters ? 'oauth_' : '').'[a-z_-]*)=(:?"([^"]*)"|([^,]*))/', $header, $matches)) {
811
+ foreach ($matches[1] as $i => $h) {
812
+ $params[$h] = Blogger_OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]);
813
+ }
814
+ if (isset($params['realm'])) {
815
+ unset($params['realm']);
816
+ }
817
+ }
818
+ return $params;
819
+ }
820
+
821
+ // helper to try to sort out headers for people who aren't running apache
822
+ public static function get_headers() {
823
+ if (function_exists('apache_request_headers')) {
824
+ // we need this to get the actual Authorization: header
825
+ // because apache tends to tell us it doesn't exist
826
+ $headers = apache_request_headers();
827
+
828
+ // sanitize the output of apache_request_headers because
829
+ // we always want the keys to be Cased-Like-This and arh()
830
+ // returns the headers in the same case as they are in the
831
+ // request
832
+ $out = array();
833
+ foreach ($headers AS $key => $value) {
834
+ $key = str_replace(
835
+ " ",
836
+ "-",
837
+ ucwords(strtolower(str_replace("-", " ", $key)))
838
+ );
839
+ $out[$key] = $value;
840
+ }
841
+ } else {
842
+ // otherwise we don't have apache and are just going to have to hope
843
+ // that $_SERVER actually contains what we need
844
+ $out = array();
845
+ if( isset($_SERVER['CONTENT_TYPE']) )
846
+ $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
847
+ if( isset($_ENV['CONTENT_TYPE']) )
848
+ $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
849
+
850
+ foreach ($_SERVER as $key => $value) {
851
+ if (substr($key, 0, 5) == "HTTP_") {
852
+ // this is chaos, basically it is just there to capitalize the first
853
+ // letter of every word that is not an initial HTTP and strip HTTP
854
+ // code from przemek
855
+ $key = str_replace(
856
+ " ",
857
+ "-",
858
+ ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
859
+ );
860
+ $out[$key] = $value;
861
+ }
862
+ }
863
+ }
864
+ return $out;
865
+ }
866
+
867
+ // This function takes a input like a=b&a=c&d=e and returns the parsed
868
+ // parameters like this
869
+ // array('a' => array('b','c'), 'd' => 'e')
870
+ public static function parse_parameters( $input ) {
871
+ if (!isset($input) || !$input) return array();
872
+
873
+ $pairs = explode('&', $input);
874
+
875
+ $parsed_parameters = array();
876
+ foreach ($pairs as $pair) {
877
+ $split = explode('=', $pair, 2);
878
+ $parameter = Blogger_OAuthUtil::urldecode_rfc3986($split[0]);
879
+ $value = isset($split[1]) ? Blogger_OAuthUtil::urldecode_rfc3986($split[1]) : '';
880
+
881
+ if (isset($parsed_parameters[$parameter])) {
882
+ // We have already recieved parameter(s) with this name, so add to the list
883
+ // of parameters with this name
884
+
885
+ if (is_scalar($parsed_parameters[$parameter])) {
886
+ // This is the first duplicate, so transform scalar (string) into an array
887
+ // so we can add the duplicates
888
+ $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
889
+ }
890
+
891
+ $parsed_parameters[$parameter][] = $value;
892
+ } else {
893
+ $parsed_parameters[$parameter] = $value;
894
+ }
895
+ }
896
+ return $parsed_parameters;
897
+ }
898
+
899
+ public static function build_http_query($params) {
900
+ if (!$params) return '';
901
+
902
+ // Urlencode both keys and values
903
+ $keys = Blogger_OAuthUtil::urlencode_rfc3986(array_keys($params));
904
+ $values = Blogger_OAuthUtil::urlencode_rfc3986(array_values($params));
905
+ $params = array_combine($keys, $values);
906
+
907
+ // Parameters are sorted by name, using lexicographical byte value ordering.
908
+ // Ref: Spec: 9.1.1 (1)
909
+ uksort($params, 'strcmp');
910
+
911
+ $pairs = array();
912
+ foreach ($params as $parameter => $value) {
913
+ if (is_array($value)) {
914
+ // If two or more parameters share the same name, they are sorted by their value
915
+ // Ref: Spec: 9.1.1 (1)
916
+ // June 12th, 2010 - changed to sort because of issue 164 by hidetaka
917
+ sort($value, SORT_STRING);
918
+ foreach ($value as $duplicate_value) {
919
+ $pairs[] = $parameter . '=' . $duplicate_value;
920
+ }
921
+ } else {
922
+ $pairs[] = $parameter . '=' . $value;
923
+ }
924
+ }
925
+ // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
926
+ // Each name-value pair is separated by an '&' character (ASCII code 38)
927
+ return implode('&', $pairs);
928
+ }
929
+ }
930
+
readme.txt CHANGED
@@ -1,135 +1,248 @@
1
- === Plugin Name ===
2
- Contributors: wordpressdotorg, Otto42, Workshopshed, SergeyBiryukov, rmccue
3
- Donate link:
4
- Tags: importer, blogger
5
- Requires at least: 3.0
6
- Tested up to: 3.6
7
- Stable tag: 0.6
8
- License: GPLv2 or later
9
-
10
- Imports posts, comments, and categories (blogger tags) from a Blogger blog then migrates authors to Wordpress users.
11
-
12
- == Description ==
13
-
14
- The Blogger Importer imports your blog data from a Blogger site into a WordPress.org installation.
15
-
16
- = Items imported =
17
-
18
- * Categories
19
- * Posts (published, scheduled and draft)
20
- * Comments (not spam)
21
-
22
- = Items not imported =
23
-
24
- * Pages
25
- * Images (the images will appear in your new blog but will link to the old blogspot or picassa web locations)
26
-
27
- == Installation ==
28
-
29
- 1. Upload the `blogger-importer` folder to the `/wp-content/plugins/` directory
30
- 1. Activate the plugin through the 'Plugins' menu in WordPress
31
-
32
- = How to use =
33
-
34
- 1. Blogger Importer is available from the WordPress Tools->Import screen.
35
- 1. Press Authorise
36
- 1. If you are not already logged into Google you will be asked to login
37
- 1. You will be asked to grant Wordpress access to your Blogger information, to continue press Grant Access
38
- 1. You will be presented with a list of all your blogs
39
- 1. Select the appropriate blog and press the import button
40
- 1. Wait whilst the posts and comments are imported
41
- 1. Press the Set Authors button
42
- 1. Select the appropriate mapping for the authors
43
- 1. Review categories, posts and comments
44
-
45
- You can now remove the importer plugin if you no longer need to use it.
46
-
47
- == Frequently Asked Questions ==
48
-
49
- = How do I re-import? =
50
-
51
- Press the clear account information button, then re-connect to blogger and re-import, the importer is designed not to re-import the same posts. If you need to do a full re-import then delete the posts and then empty the trash before re-importing.
52
-
53
- = How do I know which posts were imported? =
54
-
55
- Each of the posts loaded is tagged with a meta tags indicating where the posts were loaded from. The permalink will be set to the visible URL if the post was published or the internal ID if it was still a draft or scheduled post
56
-
57
- * blogger_author
58
- * blogger_blog
59
- * blogger_permalink
60
-
61
- = Why does it keep stopping? =
62
-
63
- The importer is designed not to overload blogger or your site so only imports in batches and will run for a fixed number of seconds before pausing, the admin screen will refresh every few seconds to show how many it has done so far. Press continue to continue importing posts and comments.
64
-
65
- = After importing there are a lot of categories =
66
-
67
- Blogger does not distinguish between tags and categories so you will likely want to review what was imported and then use the categories to tags converter
68
-
69
- = What about pages? =
70
-
71
- This importer does not handle blogger pages, you will need to manually transfer them.
72
-
73
- = What about images? =
74
-
75
- The importer will simply load the tags for the images as they appear in your source data, so you will have references to blogspot and picassa based images. If you do not migrate these with a separate tool then these will be lost when you delete your old blogger blog.
76
-
77
- = Are the permalinks the same? =
78
-
79
- No, Wordpress and Blogger handle the permalinks differently. However, it is possible to use the redirection plugin to map the old URLs across to the new URLs.
80
-
81
- = What about future posts? =
82
-
83
- The scheduled posts will be transferred and will be published as specified. However, Blogger and Wordpress handle drafts differently, Wordpress does not support dates on draft posts so you will need to use a plugin if you wish to plan your writing schedule.
84
-
85
- = My posts and comments moved across but some things are stripped out =
86
-
87
- The importer uses the SimplePie classes to process the data, these in turn use a Simplepie_Sanitize class to remove potentially malicious code from the source data.
88
-
89
- == Screenshots ==
90
-
91
- == Reference ==
92
-
93
- * https://developers.google.com/blogger/docs/1.0/developers_guide_php
94
- * https://developers.google.com/gdata/articles/oauth
95
-
96
- == Changelog ==
97
-
98
- = 0.6 =
99
- * Security improvements.
100
-
101
- = 0.5 =
102
- * Merged in fix by SergeyBiryukov http://core.trac.wordpress.org/ticket/16012
103
- * Merged in rmccue change to get_total_results to also use SimplePie from http://core.trac.wordpress.org/attachment/ticket/7652/7652-blogger.diff
104
- * Reviewed in rmccue's changes in http://core.trac.wordpress.org/attachment/ticket/7652/7652-separate.diff issues with date handling functions so skipped those
105
- * Moved SimplePie functions in new class WP_SimplePie_Blog_Item incorporating get_draft_status and get_updated and convert date
106
- * Tested comments from source blog GMT-8, destination London (currently GMT-1), comment dates transferred correctly.
107
- * Fixed typo in oauth_get
108
- * Added screen_icon() to all pages
109
- * Added GeoTags as per spec on http://codex.wordpress.org/Geodata
110
- * Change by Otto42, rmccue to use Simplepie XML processing rather than Atomparser, http://core.trac.wordpress.org/ticket/14525 ref: http://core.trac.wordpress.org/attachment/ticket/7652/7652-blogger.diff (this also fixes http://core.trac.wordpress.org/ticket/15560)
111
- * Change by Otto42 to use OAuth rather than AuthSub authentication, should make authentication more reliable
112
- * Fix by Andy from Workshopshed to load comments and nested comments correctly
113
- * Fix by Andy from Workshopshed to correctly pass the blogger start-index and max-results parameters to oAuth functions and to process more than one batch http://core.trac.wordpress.org/ticket/19096
114
- * Fix by Andy from Workshopshed error about incorrect enqueuing of scripts also changed styles to work the same
115
- * Change by Andy from Workshopshed testing in debug mode and wrapped ajax return into a function to suppress debug messages
116
- * Fix by Andy from Workshopshed notices for undefined variables.
117
- * Change by Andy from Workshopshed Added tooltip to results table to show numbers of posts and comments skipped (duplicates / missing key)
118
- * Fix by Andy from Workshopshed incorrectly checking for duplicates based on only the date and username, this gave false positives when large numbers of comments, particularly anonymous ones.
119
-
120
- = 0.4 =
121
- * Fix for tracking images being added by Blogger to non-authenticated feeds http://core.trac.wordpress.org/ticket/17623
122
-
123
- = 0.3 =
124
- * Bugfix for 403 Invalid AuthSub Token http://core.trac.wordpress.org/ticket/14629
125
-
126
- = 0.1 =
127
- * Initial release
128
-
129
- == Upgrade Notice ==
130
-
131
- = 0.5 =
132
-
133
- Merged in fixes found in Trac
134
- This version is a significant re-write based on previous versions.
135
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Blogger Importer ===
2
+ Contributors: wordpressdotorg, Otto42, Workshopshed, SergeyBiryukov, rmccue
3
+ Donate link:
4
+ Tags: importer, blogger
5
+ Requires at least: 3.0
6
+ Tested up to: 4.1
7
+ Stable tag: 0.7
8
+ License: GPLv2 or later
9
+
10
+ Imports posts, images, comments, and categories (blogger tags) from a Blogger blog then migrates authors to WordPress users.
11
+
12
+ == Description ==
13
+
14
+ The Blogger Importer imports your blog data from a Google Blogger site into a WordPress.org installation.
15
+
16
+ = Items imported =
17
+
18
+ * Categories
19
+ * Posts (published, scheduled and draft)
20
+ * Comments (not spam)
21
+ * Images
22
+
23
+ = Items not imported =
24
+
25
+ * Pages
26
+ * Widgets/Widget Data
27
+ * Templates/Theme
28
+ * Comment and author Avatars
29
+
30
+ == Installation ==
31
+
32
+ 1. Upload the `blogger-importer` folder to the `/wp-content/plugins/` directory
33
+ 1. Activate the plugin through the 'Plugins' menu in WordPress
34
+
35
+ = Prerequisites =
36
+
37
+ The importer connects your server to the blogger server to copy across the posts. For this to work you need to have connectivity from the server to the internet and also have at least one of the remote access protocols enabled, e.g. curl, streams or fsockopen. You can use the Core Control plugin to test if these are working correctly. The importer connects to Google over a secure connection so OpenSSL needs to be enabled on your server.
38
+ The importer uses the SimplePie classes to read and process the data from blogger so you will need the php-xml module installed on your webserver.
39
+
40
+ = How to use =
41
+
42
+ 1. Blogger Importer is available from the WordPress Tools->Import screen.
43
+ 1. Press Authorise
44
+ 1. If you are not already logged into Google you will be asked to login
45
+ 1. You will be asked to grant WordPress access to your Blogger information, to continue press Grant Access
46
+ 1. You will be presented with a list of all your blogs
47
+ 1. Select the appropriate blog and press the import button
48
+ 1. Wait whilst the posts, comments and images are imported
49
+ 1. Press the Set Authors button
50
+ 1. Select the appropriate mapping for the authors
51
+ 1. Review categories, posts and comments
52
+
53
+ You can now remove the importer plugin if you no longer need to use it.
54
+
55
+ == Frequently Asked Questions ==
56
+
57
+ = How do I re-import? =
58
+
59
+ Press the clear account information button, then re-connect to blogger and re-import, the importer is designed not to re-import the same posts. If you need to do a full re-import then delete the posts and then empty the trash before re-importing.
60
+
61
+ = Once I've imported the posts do I need to keep the plugin? =
62
+
63
+ No, you can remove the plugin once you've completed your migration.
64
+
65
+ = How do I know which posts were imported? =
66
+
67
+ Each of the posts loaded is tagged with a meta tags indicating where the posts were loaded from. The permalink will be set to the visible URL if the post was published or the internal ID if it was still a draft or scheduled post
68
+
69
+ * blogger_author
70
+ * blogger_blog
71
+ * blogger_permalink
72
+
73
+ = After importing there are a lot of categories =
74
+
75
+ Blogger does not distinguish between tags and categories so you will likely want to review what was imported and then use the categories to tags converter
76
+
77
+ = What about pages? =
78
+
79
+ This importer does not handle blogger pages, you will need to manually transfer them.
80
+
81
+ = What about images? =
82
+
83
+ This version of the importer imports these too, but you can disable this via a setting in the blogger-importer.php file. Tracking images of size 1x1 are not processed. If you with to specifically exclude other images you could code something for the image_filter function.
84
+
85
+ = What size are the images? =
86
+
87
+ The importer will attempt to download the a large version of the file if one is available. This is controlled by the setting "LARGE_IMAGE_SIZE" and defaults to a width of 1024. The display size of the images is the "medium" size of images as defined on Wordpress. You can change this in advance if you want to show a different size.
88
+
89
+ = How do I know what images are skipped? =
90
+
91
+ If you hover over the progress bar for images it will tell you how many images are skipped. To see the filenames of these images you will need to enable Wordpress debugging to log to file. See http://codex.wordpress.org/Debugging_in_WordPress
92
+
93
+ = What about future posts? =
94
+
95
+ The scheduled posts will be transferred and will be published as specified. However, Blogger and WordPress handle drafts differently, WordPress does not support dates on draft posts so you will need to use a plugin if you wish to plan your writing schedule.
96
+
97
+ = Are the permalinks the same? =
98
+
99
+ No, WordPress and Blogger handle the permalinks differently. However, it is possible to use the redirection plugin or your .htaccess file to map the old URLs across to the new URLs.
100
+
101
+ = My posts and comments moved across but some things are stripped out =
102
+
103
+ The importer uses the SimplePie classes to process the data, these in turn use a Simplepie_Sanitize class to remove potentially malicious code from the source data.
104
+
105
+ = The dashboard is reporting that there are 0 comments in blogger =
106
+
107
+ This can occur if your blogger blog is set to private.
108
+
109
+ = The comments don't have avatars =
110
+
111
+ This is a know limitation of the data that is provided from Blogger. The Wordpress system uses Gravatar to provide the images for the comment avatars. This relies the email of the person making the comment. Blogger does not provide the email address in the data feed so Wordpress does not display the correct images. You can manually update or script change to the comment email addresses to work around this issue.
112
+
113
+ = How do I diagnose communication issues? =
114
+
115
+ If you've got issues with the blogger importer talking to Google then you can use the Core Control plugin to first check that your communication is working with the HTTP Module and if that is all ok then check the HTTP Logging Module to see if any of the remote calls to Google are returning errors. Also the OAuth Playground is a good way to test your access to the blogger data. http://googlecodesamples.com/oauth_playground/
116
+
117
+ = I'm getting timeouts connecting to Google =
118
+
119
+ Try the core control plugin as mentioned above. Also you try changing the constant REMOTE_TIMEOUT in the blogger-importer.php file to be a larger number of seconds.
120
+
121
+ = Why does it keep stopping? =
122
+
123
+ The importer uses JQuery to process it's form so problems with it not refreshing or stalling could be due to an incompatibility with another plugin, disable any other plugins and see if the problem persists.
124
+
125
+ = It does not seem to be processing the images =
126
+
127
+ Check you've not run out of disk space on your server. Because Wordpress stores the files in multiple resolutions one image might take up as much as 250kb spread across 5 files of different sizes.
128
+
129
+ = How do I make the images bigger or smaller? =
130
+
131
+ The importer will attempt to download a large version of images but it displays them on the blog at the medium size. If you go into your settings->media options then you can display a different size "medium" image by default. You can't make this bigger than the file that has been downloaded which is where the next setting comes in.
132
+
133
+ The default size for the large images is 1024, you can change this to an even larger size by changing the following line in the blogger-import.php file.
134
+
135
+ const LARGE_IMAGE_SIZE = '1024';
136
+
137
+ The file downloaded won't be bigger than the origional file so if it was only 800x600 to start with then it won't be any bigger than that.
138
+
139
+ = I've run out of disk space processing the images =
140
+
141
+ The importer is designed to download the high resolution images where they are available. You can either disable the downloading of images or you can change the constant LARGE_IMAGE_SIZE string in the blogger-importer.php file to swap the links with a smaller image.
142
+
143
+ == Screenshots ==
144
+
145
+ 1. Import in progress
146
+ 2. Custom Fields added to Posts, Attachements and Comments
147
+
148
+ == Reference ==
149
+
150
+ * https://developers.google.com/blogger/docs/1.0/developers_guide_php
151
+ * https://developers.google.com/gdata/articles/oauth
152
+ * http://www.simplepie.org/
153
+
154
+ The following were referenced for implementing the images and links
155
+
156
+ * http://wordpress.org/extend/plugins/remote-images-grabber
157
+ * http://notions.okuda.ca/wordpress-plugins/blogger-image-import/
158
+ * http://wordpress.org/extend/plugins/cache-images/
159
+ * http://wordpress.org/extend/plugins/tumblr-importer/
160
+ * http://core.trac.wordpress.org/ticket/14525
161
+ * http://wpengineer.com/1735/easier-better-solutions-to-get-pictures-on-your-posts/
162
+ * http://www.velvetblues.com/web-development-blog/wordpress-plugin-update-urls/
163
+ * http://wordpress.stackexchange.com/questions//media-sideload-image-file-name
164
+ * http://wp.tutsplus.com/tutorials/plugins/a-guide-to-the-wordpress-http-api-the-basics/
165
+
166
+ == Known Issues ==
167
+
168
+ * Some users have reported that their IFrames are stripped out of the post content.
169
+ * Requests for better performance of larger transfers and tranfers of images
170
+ * Drop the check for OpenSSL and Blogger_OAuthSignatureMethod_RSA_SHA1 is not used by any other code in the plugin and can probably just be removed.
171
+ * Review of behavior when it re-imports, partiularly are the counts correct
172
+ * Review using get_posts or get_comments with the appropriate parameters to get the counts and exists instead of using SQL
173
+ * Incorrect notice, PHP Notice: The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. This occurs even when Iconv is installed, could be related to Blogger reporting 0 comments
174
+ * When the importer is running it's not possible to stop it using the stop button
175
+
176
+ == Changelog ==
177
+
178
+ = 0.7 =
179
+ * Fixed issue with drafts not being imported in the right state
180
+ * Added extra error handling for get_oauth_link to stop blank tokens being sent to the form
181
+ * Restructured code to keep similar steps in single function and to allow testing of components to be done
182
+ * Re-incorporated the "congrats" function and provided a sensible list of what to do next
183
+ * Add a geo_public flag to posts with geotags
184
+ * Dropped _normalize_tag after confirming that it's handled by SimplePie
185
+ * Added image handling http://core.trac.wordpress.org/ticket/4010
186
+ * Added setting author on images
187
+ * Added error handling in get_oauth_link() as suggested by daniel_henrique ref http://core.trac.wordpress.org/ticket/21163
188
+ * Added a check for OpenSSL as suggested by digitalsensus
189
+ * Fixed issue with SimplePie santizer not getting set in Wordpress 3.5
190
+ * Added filter for the congrats function 'blogger_importer_congrats' so other plugins can add in new options
191
+ * Converted manual HTML table to WP_LIST_TABLE
192
+ * Moved inline Javascript to separate file to aid debugging and testing
193
+ * Wrapped data sent to Javascript in I18n functions.
194
+ * Fixed timeout error in the Javascript, timeouts were not being used.
195
+ * Supress post revisions when importing so that DB does not grow
196
+ * Added processing of internal links
197
+ * Added uninstall.php to remove options on uninstall
198
+ * Added a timeout value to all of the wp_remote_get calls as people have reported timeout issues
199
+ * Added a setting to control the large images downloaded from blogger.
200
+ * Stopped logging all the post and comment IDs in arrays and storing in option this improved the importing of very large blogs
201
+ * Fixed issue with comment_author_IP notice
202
+ * Code restructuring to use classes for blog objects
203
+ * Changed AJAX calls to use technique described here http://codex.wordpress.org/AJAX_in_Plugins#Ajax_on_the_Administration_Side
204
+ * Added AdminURL to the greet function rather than hardcoded path
205
+ * Defaulted to turn off post pingbacks
206
+ * Fix to stop it counting pingbacks, issue reported by realdoublebee
207
+ * Retrofitted Security enhancement from 0.6, nonce added to form buttons on main screen
208
+ * Security enhancement, nonce added to form button on authors screen
209
+ * Updated POT file
210
+ * Greek Translation from Stergatou Eleni http://buddypress.org/community/members/lenasterg/
211
+
212
+ = 0.6 =
213
+ * Security enhancement, nonce added to form button on main screen
214
+
215
+ = 0.5 =
216
+ * Merged in fix by SergeyBiryukov http://core.trac.wordpress.org/ticket/16012
217
+ * Merged in rmccue change to get_total_results to also use SimplePie from http://core.trac.wordpress.org/attachment/ticket/7652/7652-blogger.diff
218
+ * Reviewed in rmccue's changes in http://core.trac.wordpress.org/attachment/ticket/7652/7652-separate.diff issues with date handling functions so skipped those
219
+ * Moved SimplePie functions in new class WP_SimplePie_Blog_Item incorporating get_draft_status and get_updated and convert date
220
+ * Tested comments from source blog GMT-8, destination London (currently GMT-1), comment dates transferred correctly.
221
+ * Fixed typo in oauth_get
222
+ * Added screen_icon() to all pages
223
+ * Added GeoTags as per spec on http://codex.wordpress.org/Geodata
224
+ * Change by Otto42, rmccue to use Simplepie XML processing rather than Atomparser, http://core.trac.wordpress.org/ticket/14525 ref: http://core.trac.wordpress.org/attachment/ticket/7652/7652-blogger.diff
225
+ this also fixes http://core.trac.wordpress.org/ticket/15560
226
+ * Change by Otto42 to use OAuth rather than AuthSub authentication, should make authentication more reliable
227
+ * Fix by Andy from Workshopshed to load comments and nested comments correctly
228
+ * Fix by Andy from Workshopshed to correctly pass the blogger start-index and max-results parameters to oAuth functions and to process more than one batch http://core.trac.wordpress.org/ticket/19096
229
+ * Fix by Andy from Workshopshed error about incorrect enqueuing of scripts also changed styles to work the same
230
+ * Change by Andy from Workshopshed testing in debug mode and wrapped ajax return into a function to suppress debug messages
231
+ * Fix by Andy from Workshopshed notices for undefined variables.
232
+ * Change by Andy from Workshopshed Added tooltip to results table to show numbers of posts and comments skipped (duplicates / missing key)
233
+ * Fix by Andy from Workshopshed incorrectly checking for duplicates based on only the date and username, this gave false positives when large numbers of comments, particularly anonymous ones.
234
+
235
+ = 0.4 =
236
+ * Fix for tracking images being added by Blogger to non-authenticated feeds http://core.trac.wordpress.org/ticket/17623
237
+
238
+ = 0.3 =
239
+ * Bugfix for 403 Invalid AuthSub Token http://core.trac.wordpress.org/ticket/14629
240
+
241
+ = 0.1 =
242
+ * Initial release
243
+
244
+ == Upgrade Notice ==
245
+
246
+ = 0.7 =
247
+
248
+ Added support for images and links. Improvements in error handling when connecting to Google. Some bug fixes, see change log. User interface refresh
uninstall.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @author Andy Clark
5
+ * @copyright 2013
6
+ *
7
+ * Tidy up things left over by the plugin
8
+ * Keep this as simple as possible so we don't stall the uninstallation process
9
+ */
10
+
11
+ delete_option('blogger_importer');
12
+ delete_option('blogger_importer_connector');
13
+ $blogopt = true;
14
+ for ($i = 1; $blogopt ; $i++) {
15
+ $blogopt = get_option('blogger_importer_blog_'.$i);
16
+ if (is_array($blogopt))
17
+ delete_option('blogger_importer_blog_'.$i);
18
+ }
19
+
20
+
21
+ ?>