]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-admin/import/livejournal.php
Wordpress 2.9.1-scripts
[autoinstalls/wordpress.git] / wp-admin / import / livejournal.php
1 <?php
2
3 /**
4  * LiveJournal API Importer
5  *
6  * @package WordPress
7  * @subpackage Importer
8  */
9
10 // XML-RPC library for communicating with LiveJournal API
11 require_once( ABSPATH . WPINC . '/class-IXR.php' );
12
13 /**
14  * LiveJournal API Importer class
15  *
16  * Imports your LiveJournal contents into WordPress using the LJ API
17  *
18  * @since 2.8
19  */
20 class LJ_API_Import {
21
22         var $comments_url = 'http://www.livejournal.com/export_comments.bml';
23         var $ixr_url      = 'http://www.livejournal.com/interface/xmlrpc';
24         var $ixr;
25         var $username;
26         var $password;
27         var $comment_meta;
28         var $comments;
29         var $usermap;
30         var $postmap;
31         var $commentmap;
32         var $pointers = array();
33
34         // This list taken from LJ, they don't appear to have an API for it
35         var $moods = array( '1' => 'aggravated',
36                                                 '10' => 'discontent',
37                                                 '100' => 'rushed',
38                                                 '101' => 'contemplative',
39                                                 '102' => 'nerdy',
40                                                 '103' => 'geeky',
41                                                 '104' => 'cynical',
42                                                 '105' => 'quixotic',
43                                                 '106' => 'crazy',
44                                                 '107' => 'creative',
45                                                 '108' => 'artistic',
46                                                 '109' => 'pleased',
47                                                 '11' => 'energetic',
48                                                 '110' => 'bitchy',
49                                                 '111' => 'guilty',
50                                                 '112' => 'irritated',
51                                                 '113' => 'blank',
52                                                 '114' => 'apathetic',
53                                                 '115' => 'dorky',
54                                                 '116' => 'impressed',
55                                                 '117' => 'naughty',
56                                                 '118' => 'predatory',
57                                                 '119' => 'dirty',
58                                                 '12' => 'enraged',
59                                                 '120' => 'giddy',
60                                                 '121' => 'surprised',
61                                                 '122' => 'shocked',
62                                                 '123' => 'rejected',
63                                                 '124' => 'numb',
64                                                 '125' => 'cheerful',
65                                                 '126' => 'good',
66                                                 '127' => 'distressed',
67                                                 '128' => 'intimidated',
68                                                 '129' => 'crushed',
69                                                 '13' => 'enthralled',
70                                                 '130' => 'devious',
71                                                 '131' => 'thankful',
72                                                 '132' => 'grateful',
73                                                 '133' => 'jealous',
74                                                 '134' => 'nervous',
75                                                 '14' => 'exhausted',
76                                                 '15' => 'happy',
77                                                 '16' => 'high',
78                                                 '17' => 'horny',
79                                                 '18' => 'hungry',
80                                                 '19' => 'infuriated',
81                                                 '2' => 'angry',
82                                                 '20' => 'irate',
83                                                 '21' => 'jubilant',
84                                                 '22' => 'lonely',
85                                                 '23' => 'moody',
86                                                 '24' => 'pissed off',
87                                                 '25' => 'sad',
88                                                 '26' => 'satisfied',
89                                                 '27' => 'sore',
90                                                 '28' => 'stressed',
91                                                 '29' => 'thirsty',
92                                                 '3' => 'annoyed',
93                                                 '30' => 'thoughtful',
94                                                 '31' => 'tired',
95                                                 '32' => 'touched',
96                                                 '33' => 'lazy',
97                                                 '34' => 'drunk',
98                                                 '35' => 'ditzy',
99                                                 '36' => 'mischievous',
100                                                 '37' => 'morose',
101                                                 '38' => 'gloomy',
102                                                 '39' => 'melancholy',
103                                                 '4' => 'anxious',
104                                                 '40' => 'drained',
105                                                 '41' => 'excited',
106                                                 '42' => 'relieved',
107                                                 '43' => 'hopeful',
108                                                 '44' => 'amused',
109                                                 '45' => 'determined',
110                                                 '46' => 'scared',
111                                                 '47' => 'frustrated',
112                                                 '48' => 'indescribable',
113                                                 '49' => 'sleepy',
114                                                 '5' => 'bored',
115                                                 '51' => 'groggy',
116                                                 '52' => 'hyper',
117                                                 '53' => 'relaxed',
118                                                 '54' => 'restless',
119                                                 '55' => 'disappointed',
120                                                 '56' => 'curious',
121                                                 '57' => 'mellow',
122                                                 '58' => 'peaceful',
123                                                 '59' => 'bouncy',
124                                                 '6' => 'confused',
125                                                 '60' => 'nostalgic',
126                                                 '61' => 'okay',
127                                                 '62' => 'rejuvenated',
128                                                 '63' => 'complacent',
129                                                 '64' => 'content',
130                                                 '65' => 'indifferent',
131                                                 '66' => 'silly',
132                                                 '67' => 'flirty',
133                                                 '68' => 'calm',
134                                                 '69' => 'refreshed',
135                                                 '7' => 'crappy',
136                                                 '70' => 'optimistic',
137                                                 '71' => 'pessimistic',
138                                                 '72' => 'giggly',
139                                                 '73' => 'pensive',
140                                                 '74' => 'uncomfortable',
141                                                 '75' => 'lethargic',
142                                                 '76' => 'listless',
143                                                 '77' => 'recumbent',
144                                                 '78' => 'exanimate',
145                                                 '79' => 'embarrassed',
146                                                 '8' => 'cranky',
147                                                 '80' => 'envious',
148                                                 '81' => 'sympathetic',
149                                                 '82' => 'sick',
150                                                 '83' => 'hot',
151                                                 '84' => 'cold',
152                                                 '85' => 'worried',
153                                                 '86' => 'loved',
154                                                 '87' => 'awake',
155                                                 '88' => 'working',
156                                                 '89' => 'productive',
157                                                 '9' => 'depressed',
158                                                 '90' => 'accomplished',
159                                                 '91' => 'busy',
160                                                 '92' => 'blah',
161                                                 '93' => 'full',
162                                                 '95' => 'grumpy',
163                                                 '96' => 'weird',
164                                                 '97' => 'nauseated',
165                                                 '98' => 'ecstatic',
166                                                 '99' => 'chipper' );
167
168         function header() {
169                 echo '<div class="wrap">';
170                 screen_icon();
171                 echo '<h2>' . __( 'Import LiveJournal' ) . '</h2>';
172         }
173
174         function footer() {
175                 echo '</div>';
176         }
177
178         function greet() {
179                 ?>
180                 <div class="narrow">
181                 <form action="admin.php?import=livejournal" method="post">
182                 <?php wp_nonce_field( 'lj-api-import' ) ?>
183                 <?php if ( get_option( 'ljapi_username' ) && get_option( 'ljapi_password' ) ) : ?>
184                         <input type="hidden" name="step" value="<?php echo esc_attr( get_option( 'ljapi_step' ) ) ?>" />
185                         <p><?php _e( 'It looks like you attempted to import your LiveJournal posts previously and got interrupted.' ) ?></p>
186                         <p class="submit">
187                                 <input type="submit" class="button-primary" value="<?php esc_attr_e( 'Continue previous import' ) ?>" />
188                         </p>
189                         <p class="submitbox"><a href="<?php echo esc_url($_SERVER['PHP_SELF'] . '?import=livejournal&amp;step=-1&amp;_wpnonce=' . wp_create_nonce( 'lj-api-import' ) . '&amp;_wp_http_referer=' . esc_attr( $_SERVER['REQUEST_URI'] )) ?>" class="deletion submitdelete"><?php _e( 'Cancel &amp; start a new import' ) ?></a></p>
190                         <p>
191                 <?php else : ?>
192                         <input type="hidden" name="step" value="1" />
193                         <input type="hidden" name="login" value="true" />
194                         <p><?php _e( 'Howdy! This importer allows you to connect directly to LiveJournal and download all your entries and comments' ) ?></p>
195                         <p><?php _e( 'Enter your LiveJournal username and password below so we can connect to your account:' ) ?></p>
196
197                         <table class="form-table">
198
199                         <tr>
200                         <th scope="row"><label for="lj_username"><?php _e( 'LiveJournal Username' ) ?></label></th>
201                         <td><input type="text" name="lj_username" id="lj_username" class="regular-text" /></td>
202                         </tr>
203
204                         <tr>
205                         <th scope="row"><label for="lj_password"><?php _e( 'LiveJournal Password' ) ?></label></th>
206                         <td><input type="password" name="lj_password" id="lj_password" class="regular-text" /></td>
207                         </tr>
208
209                         </table>
210
211                         <p><?php _e( 'If you have any entries on LiveJournal which are marked as private, they will be password-protected when they are imported so that only people who know the password can see them.' ) ?></p>
212                         <p><?php _e( 'If you don&#8217;t enter a password, ALL ENTRIES from your LiveJournal will be imported as public posts in WordPress.' ) ?></p>
213                         <p><?php _e( 'Enter the password you would like to use for all protected entries here:' ) ?></p>
214                         <table class="form-table">
215
216                         <tr>
217                         <th scope="row"><label for="protected_password"><?php _e( 'Protected Post Password' ) ?></label></th>
218                         <td><input type="text" name="protected_password" id="protected_password" class="regular-text" /></td>
219                         </tr>
220
221                         </table>
222
223                         <p><?php _e( "<strong>WARNING:</strong> This can take a really long time if you have a lot of entries in your LiveJournal, or a lot of comments. Ideally, you should only start this process if you can leave your computer alone while it finishes the import." ) ?></p>
224
225                         <p class="submit">
226                                 <input type="submit" class="button-primary" value="<?php esc_attr_e( 'Connect to LiveJournal and Import' ) ?>" />
227                         </p>
228
229                         <p><?php _e( '<strong>NOTE:</strong> If the import process is interrupted for <em>any</em> reason, come back to this page and it will continue from where it stopped automatically.' ) ?></p>
230
231                         <noscript>
232                                 <p><?php _e( '<strong>NOTE:</strong> You appear to have JavaScript disabled, so you will need to manually click through each step of this importer. If you enable JavaScript, it will step through automatically.' ) ?></p>
233                         </noscript>
234                 <?php endif; ?>
235                 </form>
236                 </div>
237                 <?php
238         }
239
240         function download_post_meta() {
241                 $total           = (int) get_option( 'ljapi_total' );
242                 $count           = (int) get_option( 'ljapi_count' );
243                 $lastsync        = get_option( 'ljapi_lastsync' );
244                 if ( !$lastsync ) {
245                         update_option( 'ljapi_lastsync', '1900-01-01 00:00:00' );
246                 }
247                 $sync_item_times = get_option( 'ljapi_sync_item_times' );
248                 if ( !is_array( $sync_item_times ) )
249                         $sync_item_times = array();
250
251                 do {
252                         $lastsync = date( 'Y-m-d H:i:s', strtotime( get_option( 'ljapi_lastsync' ) ) );
253                         $synclist = $this->lj_ixr( 'syncitems', array( 'ver' => 1, 'lastsync' => $lastsync ) );
254                         if ( is_wp_error( $synclist ) )
255                                 return $synclist;
256
257                         // Keep track of if we've downloaded everything
258                         $total = $synclist['total'];
259                         $count = $synclist['count'];
260
261                         foreach ( $synclist['syncitems'] as $event ) {
262                                 if ( substr( $event['item'], 0, 2 ) == 'L-' ) {
263                                         $sync_item_times[ str_replace( 'L-', '', $event['item'] ) ] = $event['time'];
264                                         if ( $event['time'] > $lastsync ) {
265                                                 $lastsync = $event['time'];
266                                                 update_option( 'ljapi_lastsync', $lastsync );
267                                         }
268                                 }
269                         }
270                 } while ( $total > $count );
271                 // endwhile - all post meta is cached locally
272                 unset( $synclist );
273                 update_option( 'ljapi_sync_item_times', $sync_item_times );
274                 update_option( 'ljapi_total', $total );
275                 update_option( 'ljapi_count', $count );
276
277                 echo '<p>' . __( 'Post metadata has been downloaded, proceeding with posts...' ) . '</p>';
278         }
279
280         function download_post_bodies() {
281                 $imported_count  = (int) get_option( 'ljapi_imported_count' );
282                 $sync_item_times = get_option( 'ljapi_sync_item_times' );
283                 $lastsync        = get_option( 'ljapi_lastsync_posts' );
284                 if ( !$lastsync )
285                         update_option( 'ljapi_lastsync_posts', date( 'Y-m-d H:i:s', 0 ) );
286
287                 $count = 0;
288                 echo '<ol>';
289                 do {
290                         $lastsync = date( 'Y-m-d H:i:s', strtotime( get_option( 'ljapi_lastsync_posts' ) ) );
291
292                         // Get the batch of items that match up with the syncitems list
293                         $itemlist = $this->lj_ixr( 'getevents', array( 'ver' => 1,
294                                                                                                                         'selecttype' => 'syncitems',
295                                                                                                                         'lineendings' => 'pc',
296                                                                                                                         'lastsync' => $lastsync ) );
297                         if ( is_wp_error( $itemlist ) )
298                                 return $itemlist;
299
300                         if ( $num = count( $itemlist['events'] ) ) {
301                                 for ( $e = 0; $e < count( $itemlist['events'] ); $e++ ) {
302                                         $event = $itemlist['events'][$e];
303                                         $imported_count++;
304                                         $inserted = $this->import_post( $event );
305                                         if ( is_wp_error( $inserted ) )
306                                                 return $inserted;
307                                         if ( $sync_item_times[ $event['itemid'] ] > $lastsync )
308                                                 $lastsync = $sync_item_times[ $event['itemid'] ];
309                                         wp_cache_flush();
310                                 }
311                                 update_option( 'ljapi_lastsync_posts',  $lastsync );
312                                 update_option( 'ljapi_imported_count',  $imported_count );
313                                 update_option( 'ljapi_last_sync_count', $num );
314                         }
315                         $count++;
316                 } while ( $num > 0 && $count < 3 ); // Doing up to 3 requests at a time to avoid memory problems
317
318                 // Used so that step1 knows when to stop posting back on itself
319                 update_option( 'ljapi_last_sync_count', $num );
320
321                 // Counter just used to show progress to user
322                 update_option( 'ljapi_post_batch', ( (int) get_option( 'ljapi_post_batch' ) + 1 ) );
323
324                 echo '</ol>';
325         }
326
327         function _normalize_tag( $matches ) {
328                 return '<' . strtolower( $matches[1] );
329         }
330
331         function import_post( $post ) {
332                 global $wpdb;
333
334                 // Make sure we haven't already imported this one
335                 if ( $this->get_wp_post_ID( $post['itemid'] ) )
336                         return;
337
338                 $user = wp_get_current_user();
339                 $post_author      = $user->ID;
340                 $post['security'] = !empty( $post['security'] ) ? $post['security'] : '';
341                 $post_status      = ( 'private' == trim( $post['security'] ) ) ? 'private' : 'publish'; // Only me
342                 $post_password    = ( 'usemask' == trim( $post['security'] ) ) ? $this->protected_password : ''; // "Friends" via password
343
344                 // For some reason, LJ sometimes sends a date as "2004-04-1408:38:00" (no space btwn date/time)
345                 $post_date = $post['eventtime'];
346                 if ( 18 == strlen( $post_date ) )
347                         $post_date = substr( $post_date, 0, 10 ) . ' ' . substr( $post_date, 10 );
348
349                 // Cleaning up and linking the title
350                 $post_title = isset( $post['subject'] ) ? trim( $post['subject'] ) : '';
351                 $post_title = $this->translate_lj_user( $post_title ); // Translate it, but then we'll strip the link
352                 $post_title = strip_tags( $post_title ); // Can't have tags in the title in WP
353                 $post_title = $wpdb->escape( $post_title );
354
355                 // Clean up content
356                 $post_content = $post['event'];
357                 $post_content = preg_replace_callback( '|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $post_content );
358                 // XHTMLize some tags
359                 $post_content = str_replace( '<br>', '<br />', $post_content );
360                 $post_content = str_replace( '<hr>', '<hr />', $post_content );
361                 // lj-cut ==>  <!--more-->
362                 $post_content = preg_replace( '|<lj-cut text="([^"]*)">|is', '<!--more $1-->', $post_content );
363                 $post_content = str_replace( array( '<lj-cut>', '</lj-cut>' ), array( '<!--more-->', '' ), $post_content );
364                 $first = strpos( $post_content, '<!--more' );
365                 $post_content = substr( $post_content, 0, $first + 1 ) . preg_replace( '|<!--more(.*)?-->|sUi', '', substr( $post_content, $first + 1 ) );
366                 // lj-user ==>  a href
367                 $post_content = $this->translate_lj_user( $post_content );
368                 //$post_content = force_balance_tags( $post_content );
369                 $post_content = $wpdb->escape( $post_content );
370
371                 // Handle any tags associated with the post
372                 $tags_input = !empty( $post['props']['taglist'] ) ? $post['props']['taglist'] : '';
373
374                 // Check if comments are closed on this post
375                 $comment_status = !empty( $post['props']['opt_nocomments'] ) ? 'closed' : 'open';
376
377                 echo '<li>';
378                 if ( $post_id = post_exists( $post_title, $post_content, $post_date ) ) {
379                         printf( __( 'Post <strong>%s</strong> already exists.' ), stripslashes( $post_title ) );
380                 } else {
381                         printf( __( 'Imported post <strong>%s</strong>...' ), stripslashes( $post_title ) );
382                         $postdata = compact( 'post_author', 'post_date', 'post_content', 'post_title', 'post_status', 'post_password', 'tags_input', 'comment_status' );
383                         $post_id = wp_insert_post( $postdata, true );
384                         if ( is_wp_error( $post_id ) ) {
385                                 if ( 'empty_content' == $post_id->get_error_code() )
386                                         return; // Silent skip on "empty" posts
387                                 return $post_id;
388                         }
389                         if ( !$post_id ) {
390                                 _e( 'Couldn&#8217;t get post ID (creating post failed!)' );
391                                 echo '</li>';
392                                 return new WP_Error( 'insert_post_failed', __( 'Failed to create post.' ) );
393                         }
394
395                         // Handle all the metadata for this post
396                         $this->insert_postmeta( $post_id, $post );
397                 }
398                 echo '</li>';
399         }
400
401         // Convert lj-user tags to links to that user
402         function translate_lj_user( $str ) {
403                 return preg_replace( '|<lj\s+user\s*=\s*["\']([\w-]+)["\']>|', '<a href="http://$1.livejournal.com/" class="lj-user">$1</a>', $str );
404         }
405
406         function insert_postmeta( $post_id, $post ) {
407                 // Need the original LJ id for comments
408                 add_post_meta( $post_id, 'lj_itemid', $post['itemid'] );
409
410                 // And save the permalink on LJ in case we want to link back or something
411                 add_post_meta( $post_id, 'lj_permalink', $post['url'] );
412
413                 // Supports the following "props" from LJ, saved as lj_<prop_name> in wp_postmeta
414                 //              Adult Content - adult_content
415                 //              Location - current_coords + current_location
416                 //              Mood - current_mood (translated from current_moodid)
417                 //              Music - current_music
418                 //              Userpic - picture_keyword
419                 foreach ( array( 'adult_content', 'current_coords', 'current_location', 'current_moodid', 'current_music', 'picture_keyword' ) as $prop ) {
420                         if ( !empty( $post['props'][$prop] ) ) {
421                                 if ( 'current_moodid' == $prop ) {
422                                         $prop = 'current_mood';
423                                         $val = $this->moods[ $post['props']['current_moodid'] ];
424                                 } else {
425                                         $val = $post['props'][$prop];
426                                 }
427                                 add_post_meta( $post_id, 'lj_' . $prop, $val );
428                         }
429                 }
430         }
431
432         // Set up a session (authenticate) with LJ
433         function get_session() {
434                 // Get a session via XMLRPC
435                 $cookie = $this->lj_ixr( 'sessiongenerate', array( 'ver' => 1, 'expiration' => 'short' ) );
436                 if ( is_wp_error( $cookie ) )
437                         return new WP_Error( 'cookie', __( 'Could not get a cookie from LiveJournal. Please try again soon.' ) );
438                 return new WP_Http_Cookie( array( 'name' => 'ljsession', 'value' => $cookie['ljsession'] ) );
439         }
440
441         // Loops through and gets comment meta from LJ in batches
442         function download_comment_meta() {
443                 $cookie = $this->get_session();
444                 if ( is_wp_error( $cookie ) )
445                         return $cookie;
446
447                 // Load previous state (if any)
448                 $this->usermap = (array) get_option( 'ljapi_usermap' );
449                 $maxid         = get_option( 'ljapi_maxid' ) ? get_option( 'ljapi_maxid' ) : 1;
450                 $highest_id    = get_option( 'ljapi_highest_id' ) ? get_option( 'ljapi_highest_id' ) : 0;
451
452                 // We need to loop over the metadata request until we have it all
453                 while ( $maxid > $highest_id ) {
454                         // Now get the meta listing
455                         $results = wp_remote_get( $this->comments_url . '?get=comment_meta&startid=' . ( $highest_id + 1 ),
456                                                                                 array( 'cookies' => array( $cookie ), 'timeout' => 20 ) );
457                         if ( is_wp_error( $results ) )
458                                 return new WP_Error( 'comment_meta', __( 'Failed to retrieve comment meta information from LiveJournal. Please try again soon.' ) );
459
460                         $results = wp_remote_retrieve_body( $results );
461
462                         // Get the maxid so we know if we have them all yet
463                         preg_match( '|<maxid>(\d+)</maxid>|', $results, $matches );
464                         if ( 0 == $matches[1] ) {
465                                 // No comment meta = no comments for this journal
466                                 echo '<p>' . __( 'You have no comments to import!' ) . '</p>';
467                                 update_option( 'ljapi_highest_id', 1 );
468                                 update_option( 'ljapi_highest_comment_id', 1 );
469                                 return false; // Bail out of comment importing entirely
470                         }
471                         $maxid = !empty( $matches[1] ) ? $matches[1] : $maxid;
472
473                         // Parse comments and get highest id available
474                         preg_match_all( '|<comment id=\'(\d+)\'|is', $results, $matches );
475                         foreach ( $matches[1] as $id ) {
476                                 if ( $id > $highest_id )
477                                         $highest_id = $id;
478                         }
479
480                         // Parse out the list of user mappings, and add it to the known list
481                         preg_match_all( '|<usermap id=\'(\d+)\' user=\'([^\']+)\' />|', $results, $matches );
482                         foreach ( $matches[1] as $count => $userid )
483                                 $this->usermap[$userid] = $matches[2][$count]; // need this in memory for translating ids => names
484
485                         wp_cache_flush();
486                 }
487                 // endwhile - should have seen all comment meta at this point
488
489                 update_option( 'ljapi_usermap',    $this->usermap );
490                 update_option( 'ljapi_maxid',      $maxid );
491                 update_option( 'ljapi_highest_id', $highest_id );
492
493                 echo '<p>' . __( ' Comment metadata downloaded successfully, proceeding with comment bodies...' ) . '</p>';
494
495                 return true;
496         }
497
498         // Downloads actual comment bodies from LJ
499         // Inserts them all directly to the DB, with additional info stored in "spare" fields
500         function download_comment_bodies() {
501                 global $wpdb;
502                 $cookie = $this->get_session();
503                 if ( is_wp_error( $cookie ) )
504                         return $cookie;
505
506                 // Load previous state (if any)
507                 $this->usermap = (array) get_option( 'ljapi_usermap' );
508                 $maxid         = get_option( 'ljapi_maxid' ) ? (int) get_option( 'ljapi_maxid' ) : 1;
509                 $highest_id    = (int) get_option( 'ljapi_highest_comment_id' );
510                 $loop = 0;
511                 while ( $maxid > $highest_id && $loop < 5 ) { // We do 5 loops per call to avoid memory limits
512                         $loop++;
513
514                         // Get a batch of comments, using the highest_id we've already got as a starting point
515                         $results = wp_remote_get( $this->comments_url . '?get=comment_body&startid=' . ( $highest_id + 1 ),
516                                                                                 array( 'cookies' => array( $cookie ), 'timeout' => 20 ) );
517                         if ( is_wp_error( $results ) )
518                                 return new WP_Error( 'comment_bodies', __( 'Failed to retrieve comment bodies from LiveJournal. Please try again soon.' ) );
519
520                         $results = wp_remote_retrieve_body( $results );
521
522                         // Parse out each comment and insert directly
523                         preg_match_all( '|<comment id=\'(\d+)\'.*</comment>|iUs', $results, $matches );
524                         for ( $c = 0; $c < count( $matches[0] ); $c++ ) {
525                                 // Keep track of highest id seen
526                                 if ( $matches[1][$c] > $highest_id ) {
527                                         $highest_id = $matches[1][$c];
528                                         update_option( 'ljapi_highest_comment_id', $highest_id );
529                                 }
530
531                                 $comment = $matches[0][$c];
532
533                                 // Filter out any captured, deleted comments (nothing useful to import)
534                                 $comment = preg_replace( '|<comment id=\'\d+\' jitemid=\'\d+\' posterid=\'\d+\' state=\'D\'[^/]*/>|is', '', $comment );
535
536                                 // Parse this comment into an array and insert
537                                 $comment = $this->parse_comment( $comment );
538                                 $comment = wp_filter_comment( $comment );
539                                 $id = wp_insert_comment( $comment );
540
541                                 // Clear cache
542                                 clean_comment_cache( $id );
543                         }
544
545                         // Clear cache to preseve memory
546                         wp_cache_flush();
547                 }
548                 // endwhile - all comments downloaded and ready for bulk processing
549
550                 // Counter just used to show progress to user
551                 update_option( 'ljapi_comment_batch', ( (int) get_option( 'ljapi_comment_batch' ) + 1 ) );
552
553                 return true;
554         }
555
556         // Takes a block of XML and parses out all the elements of the comment
557         function parse_comment( $comment ) {
558                 global $wpdb;
559
560                 // Get the top-level attributes
561                 preg_match( '|<comment([^>]+)>|i', $comment, $attribs );
562                 preg_match( '| id=\'(\d+)\'|i', $attribs[1], $matches );
563                 $lj_comment_ID = $matches[1];
564                 preg_match( '| jitemid=\'(\d+)\'|i', $attribs[1], $matches );
565                 $lj_comment_post_ID = $matches[1];
566                 preg_match( '| posterid=\'(\d+)\'|i', $attribs[1], $matches );
567                 $comment_author_ID = isset( $matches[1] ) ? $matches[1] : 0;
568                 preg_match( '| parentid=\'(\d+)\'|i', $attribs[1], $matches ); // optional
569                 $lj_comment_parent = isset( $matches[1] ) ? $matches[1] : 0;
570                 preg_match( '| state=\'([SDFA])\'|i', $attribs[1], $matches ); // optional
571                 $lj_comment_state = isset( $matches[1] ) ? $matches[1] : 'A';
572
573                 // Clean up "subject" - this will become the first line of the comment in WP
574                 preg_match( '|<subject>(.*)</subject>|is', $comment, $matches );
575                 if ( isset( $matches[1] ) ) {
576                         $comment_subject = $wpdb->escape( trim( $matches[1] ) );
577                         if ( 'Re:' == $comment_subject )
578                                 $comment_subject = '';
579                 }
580
581                 // Get the body and HTMLize it
582                 preg_match( '|<body>(.*)</body>|is', $comment, $matches );
583                 $comment_content = !empty( $comment_subject ) ? $comment_subject . "\n\n" . $matches[1] : $matches[1];
584                 $comment_content = @html_entity_decode( $comment_content, ENT_COMPAT, get_option('blog_charset') );
585                 $comment_content = str_replace( '&apos;', "'", $comment_content );
586                 $comment_content = wpautop( $comment_content );
587                 $comment_content = str_replace( '<br>', '<br />', $comment_content );
588                 $comment_content = str_replace( '<hr>', '<hr />', $comment_content );
589                 $comment_content = preg_replace_callback( '|<(/?[A-Z]+)|', array( &$this, '_normalize_tag' ), $comment_content );
590                 $comment_content = $wpdb->escape( trim( $comment_content ) );
591
592                 // Get and convert the date
593                 preg_match( '|<date>(.*)</date>|i', $comment, $matches );
594                 $comment_date = trim( str_replace( array( 'T', 'Z' ), ' ', $matches[1] ) );
595
596                 // Grab IP if available
597                 preg_match( '|<property name=\'poster_ip\'>(.*)</property>|i', $comment, $matches ); // optional
598                 $comment_author_IP = isset( $matches[1] ) ? $matches[1] : '';
599
600                 // Try to get something useful for the comment author, especially if it was "my" comment
601                 $author = ( empty( $comment_author_ID ) || empty( $this->usermap[$comment_author_ID] ) || substr( $this->usermap[$comment_author_ID], 0, 4 ) == 'ext_' ) ? __( 'Anonymous' ) : $this->usermap[$comment_author_ID];
602                 if ( get_option( 'ljapi_username' ) == $author ) {
603                         $user    = wp_get_current_user();
604                         $user_id = $user->ID;
605                         $author  = $user->display_name;
606                         $url     = trailingslashit( get_option( 'home' ) );
607                 } else {
608                         $user_id = 0;
609                         $url     = ( __( 'Anonymous' ) == $author ) ? '' : 'http://' . $author . '.livejournal.com/';
610                 }
611
612                 // Send back the array of details
613                 return array( 'lj_comment_ID' => $lj_comment_ID,
614                                                 'lj_comment_post_ID' => $lj_comment_post_ID,
615                                                 'lj_comment_parent' => ( !empty( $lj_comment_parent ) ? $lj_comment_parent : 0 ),
616                                                 'lj_comment_state' => $lj_comment_state,
617                                                 'comment_post_ID' => $this->get_wp_post_ID( $lj_comment_post_ID ),
618                                                 'comment_author' => $author,
619                                                 'comment_author_url' => $url,
620                                                 'comment_author_email' => '',
621                                                 'comment_content' => $comment_content,
622                                                 'comment_date' => $comment_date,
623                                                 'comment_author_IP' => ( !empty( $comment_author_IP ) ? $comment_author_IP : '' ),
624                                                 'comment_approved' => ( in_array( $lj_comment_state, array( 'A', 'F' ) ) ? 1 : 0 ),
625                                                 'comment_karma' => $lj_comment_ID, // Need this and next value until rethreading is done
626                                                 'comment_agent' => $lj_comment_parent,
627                                                 'comment_type' => 'livejournal',  // Custom type, so we can find it later for processing
628                                                 'user_ID' => $user_id
629                                         );
630         }
631
632
633         // Gets the post_ID that a LJ post has been saved as within WP
634         function get_wp_post_ID( $post ) {
635                 global $wpdb;
636
637                 if ( empty( $this->postmap[$post] ) )
638                         $this->postmap[$post] = (int) $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'lj_itemid' AND meta_value = %d", $post ) );
639
640                 return $this->postmap[$post];
641         }
642
643         // Gets the comment_ID that a LJ comment has been saved as within WP
644         function get_wp_comment_ID( $comment ) {
645                 global $wpdb;
646                 if ( empty( $this->commentmap[$comment] ) )
647                         $this->commentmap[$comment] = $wpdb->get_var( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_karma = %d", $comment ) );
648                 return $this->commentmap[$comment];
649         }
650
651         function lj_ixr() {
652                 if ( $challenge = $this->ixr->query( 'LJ.XMLRPC.getchallenge' ) ) {
653                         $challenge = $this->ixr->getResponse();
654                 }
655                 if ( isset( $challenge['challenge'] ) ) {
656                         $params = array( 'username' => $this->username,
657                                                         'auth_method' => 'challenge',
658                                                         'auth_challenge' => $challenge['challenge'],
659                                                         'auth_response' => md5( $challenge['challenge'] . md5( $this->password ) ) );
660                 } else {
661                         return new WP_Error( 'IXR', __( 'LiveJournal is not responding to authentication requests. Please wait a while and then try again.' ) );
662                 }
663
664                 $args = func_get_args();
665         $method = array_shift( $args );
666                 if ( isset( $args[0] ) )
667                         $params = array_merge( $params, $args[0] );
668                 if ( $this->ixr->query( 'LJ.XMLRPC.' . $method, $params ) ) {
669                         return $this->ixr->getResponse();
670                 } else {
671                         return new WP_Error( 'IXR', __( 'XML-RPC Request Failed -- ' ) . $this->ixr->getErrorCode() . ': ' . $this->ixr->getErrorMessage() );
672                 }
673         }
674
675         function dispatch() {
676                 if ( empty( $_REQUEST['step'] ) )
677                         $step = 0;
678                 else
679                         $step = (int) $_REQUEST['step'];
680
681                 $this->header();
682
683                 switch ( $step ) {
684                         case -1 :
685                                 $this->cleanup();
686                                 // Intentional no break
687                         case 0 :
688                                 $this->greet();
689                                 break;
690                         case 1 :
691                         case 2 :
692                         case 3 :
693                                 check_admin_referer( 'lj-api-import' );
694                                 $result = $this->{ 'step' . $step }();
695                                 if ( is_wp_error( $result ) ) {
696                                         $this->throw_error( $result, $step );
697                                 }
698                                 break;
699                 }
700
701                 $this->footer();
702         }
703
704         // Technically the first half of step 1, this is separated to allow for AJAX
705         // calls. Sets up some variables and options and confirms authentication.
706         function setup() {
707                 global $verified;
708                 // Get details from form or from DB
709                 if ( !empty( $_POST['lj_username'] ) && !empty( $_POST['lj_password'] ) ) {
710                         // Store details for later
711                         $this->username = $_POST['lj_username'];
712                         $this->password = $_POST['lj_password'];
713                         update_option( 'ljapi_username', $this->username );
714                         update_option( 'ljapi_password', $this->password );
715                 } else {
716                         $this->username = get_option( 'ljapi_username' );
717                         $this->password = get_option( 'ljapi_password' );
718                 }
719
720                 // This is the password to set on protected posts
721                 if ( !empty( $_POST['protected_password'] ) ) {
722                         $this->protected_password = $_POST['protected_password'];
723                         update_option( 'ljapi_protected_password', $this->protected_password );
724                 } else {
725                         $this->protected_password = get_option( 'ljapi_protected_password' );
726                 }
727
728                 // Log in to confirm the details are correct
729                 if ( empty( $this->username ) || empty( $this->password ) ) {
730                         ?>
731                         <p><?php _e( 'Please enter your LiveJournal username <em>and</em> password so we can download your posts and comments.' ) ?></p>
732                         <p><a href="<?php echo esc_url($_SERVER['PHP_SELF'] . '?import=livejournal&amp;step=-1&amp;_wpnonce=' . wp_create_nonce( 'lj-api-import' ) . '&amp;_wp_http_referer=' . esc_attr( str_replace( '&step=1', '', $_SERVER['REQUEST_URI'] ) ) ) ?>"><?php _e( 'Start again' ) ?></a></p>
733                         <?php
734                         return false;
735                 }
736                 $verified = $this->lj_ixr( 'login' );
737                 if ( is_wp_error( $verified ) ) {
738                         if ( 100 == $this->ixr->getErrorCode() || 101 == $this->ixr->getErrorCode() ) {
739                                 delete_option( 'ljapi_username' );
740                                 delete_option( 'ljapi_password' );
741                                 delete_option( 'ljapi_protected_password' );
742                                 ?>
743                                 <p><?php _e( 'Logging in to LiveJournal failed. Check your username and password and try again.' ) ?></p>
744                                 <p><a href="<?php echo esc_url($_SERVER['PHP_SELF'] . '?import=livejournal&amp;step=-1&amp;_wpnonce=' . wp_create_nonce( 'lj-api-import' ) . '&amp;_wp_http_referer=' . esc_attr( str_replace( '&step=1', '', $_SERVER['REQUEST_URI'] ) ) ) ?>"><?php _e( 'Start again' ) ?></a></p>
745                                 <?php
746                                 return false;
747                         } else {
748                                 return $verified;
749                         }
750                 } else {
751                         update_option( 'ljapi_verified', 'yes' );
752                 }
753
754                 // Set up some options to avoid them autoloading (these ones get big)
755                 add_option( 'ljapi_sync_item_times',  '', '', 'no' );
756                 add_option( 'ljapi_usermap',          '', '', 'no' );
757                 update_option( 'ljapi_comment_batch', 0 );
758
759                 return true;
760         }
761
762         // Check form inputs and start importing posts
763         function step1() {
764                 global $verified;
765                 set_time_limit( 0 );
766                 update_option( 'ljapi_step', 1 );
767                 if ( !$this->ixr ) $this->ixr = new IXR_Client( $this->ixr_url, false, 80, 30 );
768                 if ( empty( $_POST['login'] ) ) {
769                         // We're looping -- load some details from DB
770                         $this->username = get_option( 'ljapi_username' );
771                         $this->password = get_option( 'ljapi_password' );
772                         $this->protected_password = get_option( 'ljapi_protected_password' );
773                 } else {
774                         // First run (non-AJAX)
775                         $setup = $this->setup();
776                         if ( !$setup ) {
777                                 return false;
778                         } else if ( is_wp_error( $setup ) ) {
779                                 $this->throw_error( $setup, 1 );
780                                 return false;
781                         }
782                 }
783
784                 echo '<div id="ljapi-status">';
785                 echo '<h3>' . __( 'Importing Posts' ) . '</h3>';
786                 echo '<p>' . __( 'We&#8217;re downloading and importing your LiveJournal posts...' ) . '</p>';
787                 if ( get_option( 'ljapi_post_batch' ) && count( get_option( 'ljapi_sync_item_times' ) ) ) {
788                         $batch = count( get_option( 'ljapi_sync_item_times' ) );
789                         $batch = $count > 300 ? ceil( $batch / 300 ) : 1;
790                         echo '<p><strong>' . sprintf( __( 'Imported post batch %d of <strong>approximately</strong> %d' ), ( get_option( 'ljapi_post_batch' ) + 1 ), $batch ) . '</strong></p>';
791                 }
792                 ob_flush(); flush();
793
794                 if ( !get_option( 'ljapi_lastsync' ) || '1900-01-01 00:00:00' == get_option( 'ljapi_lastsync' ) ) {
795                         // We haven't downloaded meta yet, so do that first
796                         $result = $this->download_post_meta();
797                         if ( is_wp_error( $result ) ) {
798                                 $this->throw_error( $result, 1 );
799                                 return false;
800                         }
801                 }
802
803                 // Download a batch of actual posts
804                 $result = $this->download_post_bodies();
805                 if ( is_wp_error( $result ) ) {
806                         if ( 406 == $this->ixr->getErrorCode() ) {
807                                 ?>
808                                 <p><strong><?php _e( 'Uh oh &ndash; LiveJournal has disconnected us because we made too many requests to their servers too quickly.' ) ?></strong></p>
809                                 <p><strong><?php _e( 'We&#8217;ve saved where you were up to though, so if you come back to this importer in about 30 minutes, you should be able to continue from where you were.' ) ?></strong></p>
810                                 <?php
811                                 echo $this->next_step( 1, __( 'Try Again' ) );
812                                 return false;
813                         } else {
814                                 $this->throw_error( $result, 1 );
815                                 return false;
816                         }
817                 }
818
819                 if ( get_option( 'ljapi_last_sync_count' ) > 0 ) {
820                 ?>
821                         <form action="admin.php?import=livejournal" method="post" id="ljapi-auto-repost">
822                         <?php wp_nonce_field( 'lj-api-import' ) ?>
823                         <input type="hidden" name="step" id="step" value="1" />
824                         <p><input type="submit" class="button-primary" value="<?php esc_attr_e( 'Import the next batch' ) ?>" /> <span id="auto-message"></span></p>
825                         </form>
826                         <?php $this->auto_ajax( 'ljapi-auto-repost', 'auto-message', 0 ); ?>
827                 <?php
828                 } else {
829                         echo '<p>' . __( 'Your posts have all been imported, but wait &#8211; there&#8217;s more! Now we need to download &amp; import your comments.' ) . '</p>';
830                         echo $this->next_step( 2, __( 'Download my comments &raquo;' ) );
831                         $this->auto_submit();
832                 }
833                 echo '</div>';
834         }
835
836         // Download comments to local XML
837         function step2() {
838                 set_time_limit( 0 );
839                 update_option( 'ljapi_step', 2 );
840                 $this->username = get_option( 'ljapi_username' );
841                 $this->password = get_option( 'ljapi_password' );
842                 $this->ixr = new IXR_Client( $this->ixr_url, false, 80, 30 );
843
844                 echo '<div id="ljapi-status">';
845                 echo '<h3>' . __( 'Downloading Comments' ) . '</h3>';
846                 echo '<p>' . __( 'Now we will download your comments so we can import them (this could take a <strong>long</strong> time if you have lots of comments)...' ) . '</p>';
847                 ob_flush(); flush();
848
849                 if ( !get_option( 'ljapi_usermap' ) ) {
850                         // We haven't downloaded meta yet, so do that first
851                         $result = $this->download_comment_meta();
852                         if ( is_wp_error( $result ) ) {
853                                 $this->throw_error( $result, 2 );
854                                 return false;
855                         }
856                 }
857
858                 // Download a batch of actual comments
859                 $result = $this->download_comment_bodies();
860                 if ( is_wp_error( $result ) ) {
861                         $this->throw_error( $result, 2 );
862                         return false;
863                 }
864
865                 $maxid      = get_option( 'ljapi_maxid' ) ? (int) get_option( 'ljapi_maxid' ) : 1;
866                 $highest_id = (int) get_option( 'ljapi_highest_comment_id' );
867                 if ( $maxid > $highest_id ) {
868                         $batch = $maxid > 5000 ? ceil( $maxid / 5000 ) : 1;
869                 ?>
870                         <form action="admin.php?import=livejournal" method="post" id="ljapi-auto-repost">
871                         <p><strong><?php printf( __( 'Imported comment batch %d of <strong>approximately</strong> %d' ), get_option( 'ljapi_comment_batch' ), $batch ) ?></strong></p>
872                         <?php wp_nonce_field( 'lj-api-import' ) ?>
873                         <input type="hidden" name="step" id="step" value="2" />
874                         <p><input type="submit" class="button-primary" value="<?php esc_attr_e( 'Import the next batch' ) ?>" /> <span id="auto-message"></span></p>
875                         </form>
876                         <?php $this->auto_ajax( 'ljapi-auto-repost', 'auto-message', 0 ); ?>
877                 <?php
878                 } else {
879                         echo '<p>' . __( 'Your comments have all been imported now, but we still need to rebuild your conversation threads.' ) . '</p>';
880                         echo $this->next_step( 3, __( 'Rebuild my comment threads &raquo;' ) );
881                         $this->auto_submit();
882                 }
883                 echo '</div>';
884         }
885
886         // Re-thread comments already in the DB
887         function step3() {
888                 global $wpdb;
889                 set_time_limit( 0 );
890                 update_option( 'ljapi_step', 3 );
891
892                 echo '<div id="ljapi-status">';
893                 echo '<h3>' . __( 'Threading Comments' ) . '</h3>';
894                 echo '<p>' . __( 'We are now re-building the threading of your comments (this can also take a while if you have lots of comments)...' ) . '</p>';
895                 ob_flush(); flush();
896
897                 // Only bother adding indexes if they have over 5000 comments (arbitrary number)
898                 $imported_comments = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_type = 'livejournal'" );
899                 $added_indices = false;
900                 if ( 5000 < $imported_comments ) {
901                         include_once(ABSPATH . 'wp-admin/includes/upgrade.php');
902                         $added_indices = true;
903                         add_clean_index( $wpdb->comments, 'comment_type'  );
904                         add_clean_index( $wpdb->comments, 'comment_karma' );
905                         add_clean_index( $wpdb->comments, 'comment_agent' );
906                 }
907
908                 // Get LJ comments, which haven't been threaded yet, 5000 at a time and thread them
909                 while ( $comments = $wpdb->get_results( "SELECT comment_ID, comment_agent FROM {$wpdb->comments} WHERE comment_type = 'livejournal' AND comment_agent != '0' LIMIT 5000", OBJECT ) ) {
910                         foreach ( $comments as $comment ) {
911                                 $wpdb->update( $wpdb->comments,
912                                                                 array( 'comment_parent' => $this->get_wp_comment_ID( $comment->comment_agent ), 'comment_type' => 'livejournal-done' ),
913                                                                 array( 'comment_ID' => $comment->comment_ID ) );
914                         }
915                         wp_cache_flush();
916                         $wpdb->flush();
917                 }
918
919                 // Revert the comments table back to normal and optimize it to reclaim space
920                 if ( $added_indices ) {
921                         drop_index( $wpdb->comments, 'comment_type'  );
922                         drop_index( $wpdb->comments, 'comment_karma' );
923                         drop_index( $wpdb->comments, 'comment_agent' );
924                         $wpdb->query( "OPTIMIZE TABLE {$wpdb->comments}" );
925                 }
926
927                 // Clean up database and we're out
928                 $this->cleanup();
929                 do_action( 'import_done', 'livejournal' );
930                 if ( $imported_comments > 1 )
931                         echo '<p>' . sprintf( __( "Successfully re-threaded %s comments." ), number_format( $imported_comments ) ) . '</p>';
932                 echo '<h3>';
933                 printf( __( 'All done. <a href="%s">Have fun!</a>' ), get_option( 'home' ) );
934                 echo '</h3>';
935                 echo '</div>';
936         }
937
938         // Output an error message with a button to try again.
939         function throw_error( $error, $step ) {
940                 echo '<p><strong>' . $error->get_error_message() . '</strong></p>';
941                 echo $this->next_step( $step, __( 'Try Again' ) );
942         }
943
944         // Returns the HTML for a link to the next page
945         function next_step( $next_step, $label, $id = 'ljapi-next-form' ) {
946                 $str  = '<form action="admin.php?import=livejournal" method="post" id="' . $id . '">';
947                 $str .= wp_nonce_field( 'lj-api-import', '_wpnonce', true, false );
948                 $str .= wp_referer_field( false );
949                 $str .= '<input type="hidden" name="step" id="step" value="' . esc_attr($next_step) . '" />';
950                 $str .= '<p><input type="submit" class="button-primary" value="' . esc_attr( $label ) . '" /> <span id="auto-message"></span></p>';
951                 $str .= '</form>';
952
953                 return $str;
954         }
955
956         // Automatically submit the specified form after $seconds
957         // Include a friendly countdown in the element with id=$msg
958         function auto_submit( $id = 'ljapi-next-form', $msg = 'auto-message', $seconds = 10 ) {
959                 ?><script type="text/javascript">
960                         next_counter = <?php echo $seconds ?>;
961                         jQuery(document).ready(function(){
962                                 ljapi_msg();
963                         });
964
965                         function ljapi_msg() {
966                                 str = '<?php _e( "Continuing in %d" ) ?>';
967                                 jQuery( '#<?php echo $msg ?>' ).text( str.replace( /%d/, next_counter ) );
968                                 if ( next_counter <= 0 ) {
969                                         if ( jQuery( '#<?php echo $id ?>' ).length ) {
970                                                 jQuery( "#<?php echo $id ?> input[type='submit']" ).hide();
971                                                 str = '<?php _e( "Continuing" ) ?> <img src="images/wpspin_light.gif" alt="" id="processing" align="top" />';
972                                                 jQuery( '#<?php echo $msg ?>' ).html( str );
973                                                 jQuery( '#<?php echo $id ?>' ).submit();
974                                                 return;
975                                         }
976                                 }
977                                 next_counter = next_counter - 1;
978                                 setTimeout('ljapi_msg()', 1000);
979                         }
980                 </script><?php
981         }
982
983         // Automatically submit the form with #id to continue the process
984         // Hide any submit buttons to avoid people clicking them
985         // Display a countdown in the element indicated by $msg for "Continuing in x"
986         function auto_ajax( $id = 'ljapi-next-form', $msg = 'auto-message', $seconds = 5 ) {
987                 ?><script type="text/javascript">
988                         next_counter = <?php echo $seconds ?>;
989                         jQuery(document).ready(function(){
990                                 ljapi_msg();
991                         });
992
993                         function ljapi_msg() {
994                                 str = '<?php _e( "Continuing in %d" ) ?>';
995                                 jQuery( '#<?php echo $msg ?>' ).text( str.replace( /%d/, next_counter ) );
996                                 if ( next_counter <= 0 ) {
997                                         if ( jQuery( '#<?php echo $id ?>' ).length ) {
998                                                 jQuery( "#<?php echo $id ?> input[type='submit']" ).hide();
999                                                 jQuery.ajaxSetup({'timeout':3600000});
1000                                                 str = '<?php _e( "Processing next batch." ) ?> <img src="images/wpspin_light.gif" alt="" id="processing" align="top" />';
1001                                                 jQuery( '#<?php echo $msg ?>' ).html( str );
1002                                                 jQuery('#ljapi-status').load(ajaxurl, {'action':'lj-importer',
1003                                                                                                                                 'step':jQuery('#step').val(),
1004                                                                                                                                 '_wpnonce':'<?php echo wp_create_nonce( 'lj-api-import' ) ?>',
1005                                                                                                                                 '_wp_http_referer':'<?php echo $_SERVER['REQUEST_URI'] ?>'});
1006                                                 return;
1007                                         }
1008                                 }
1009                                 next_counter = next_counter - 1;
1010                                 setTimeout('ljapi_msg()', 1000);
1011                         }
1012                 </script><?php
1013         }
1014
1015         // Remove all options used during import process and
1016         // set wp_comments entries back to "normal" values
1017         function cleanup() {
1018                 global $wpdb;
1019
1020                 delete_option( 'ljapi_username' );
1021                 delete_option( 'ljapi_password' );
1022                 delete_option( 'ljapi_protected_password' );
1023                 delete_option( 'ljapi_verified' );
1024                 delete_option( 'ljapi_total' );
1025                 delete_option( 'ljapi_count' );
1026                 delete_option( 'ljapi_lastsync' );
1027                 delete_option( 'ljapi_last_sync_count' );
1028                 delete_option( 'ljapi_sync_item_times' );
1029                 delete_option( 'ljapi_lastsync_posts' );
1030                 delete_option( 'ljapi_post_batch' );
1031                 delete_option( 'ljapi_imported_count' );
1032                 delete_option( 'ljapi_maxid' );
1033                 delete_option( 'ljapi_usermap' );
1034                 delete_option( 'ljapi_highest_id' );
1035                 delete_option( 'ljapi_highest_comment_id' );
1036                 delete_option( 'ljapi_comment_batch' );
1037                 delete_option( 'ljapi_step' );
1038
1039                 $wpdb->update( $wpdb->comments,
1040                                                 array( 'comment_karma' => 0, 'comment_agent' => 'WP LJ Importer', 'comment_type' => '' ),
1041                                                 array( 'comment_type' => 'livejournal-done' ) );
1042                 $wpdb->update( $wpdb->comments,
1043                                                 array( 'comment_karma' => 0, 'comment_agent' => 'WP LJ Importer', 'comment_type' => '' ),
1044                                                 array( 'comment_type' => 'livejournal' ) );
1045         }
1046
1047         function LJ_API_Import() {
1048                 $this->__construct();
1049         }
1050
1051         function __construct() {
1052                 // Nothing
1053         }
1054 }
1055
1056 $lj_api_import = new LJ_API_Import();
1057
1058 register_importer( 'livejournal', __( 'LiveJournal' ), __( 'Import posts from LiveJournal using their API.' ), array( $lj_api_import, 'dispatch' ) );
1059 ?>