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