WordPress.org

Make WordPress Core

Ticket #8999: db-based-lj-importer.diff

File db-based-lj-importer.diff, 39.4 KB (added by beaulebens, 9 years ago)

DB-based version, with some optimizations from azaozz.

  • livejournal.php

     
    11<?php
     2
    23/**
    34 * LiveJournal API Importer
    45 *
     
    910// XML-RPC library for communicating with LiveJournal API
    1011require_once( ABSPATH . WPINC . '/class-IXR.php' );
    1112
    12 // Snoopy for getting comments (with cookies)
    13 require_once( ABSPATH . WPINC . '/class-snoopy.php' );
    14 
    1513/**
    1614 * LiveJournal API Importer class
    1715 *
     
    2624        var $ixr;
    2725        var $username;
    2826        var $password;
    29         var $snoop;
    3027        var $comment_meta;
    3128        var $comments;
    3229        var $usermap;
    3330        var $postmap;
     31        var $commentmap;
    3432        var $pointers = array();
    3533       
    3634        // This list taken from LJ, they don't appear to have an API for it
     
    188186                        <p class="submit">
    189187                                <input type="submit" class="button-primary" value="<?php echo attribute_escape( __( 'Continue previous import' ) ) ?>" />
    190188                        </p>
    191                         <p><a href="<?php echo $_SERVER['PHP_SELF'] . '?import=livejournal&amp;step=-1&amp;_wpnonce=' . wp_create_nonce( 'lj-api-import' ) . '&amp;_wp_http_referer=' . attribute_escape( $_SERVER['REQUEST_URI'] ) ?>"><?php _e( 'Cancel &amp; start a new import' ) ?></a></p>
     189                        <p class="submitbox"><a href="<?php echo $_SERVER['PHP_SELF'] . '?import=livejournal&amp;step=-1&amp;_wpnonce=' . wp_create_nonce( 'lj-api-import' ) . '&amp;_wp_http_referer=' . attribute_escape( $_SERVER['REQUEST_URI'] ) ?>" class="deletion submitdelete"><?php _e( 'Cancel &amp; start a new import' ) ?></a></p>
    192190                        <p>
    193191                <?php else : ?>
    194192                        <input type="hidden" name="step" value="1" />
     
    228226                        </p>
    229227               
    230228                        <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>
     229
     230                        <noscript>
     231                                <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>
     232                        </noscript>
    231233                <?php endif; ?>
    232234                </form>
    233235                </div>
     
    248250                do {
    249251                        $lastsync = date( 'Y-m-d H:i:s', strtotime( get_option( 'ljapi_lastsync' ) ) );
    250252                        $synclist = $this->lj_ixr( 'syncitems', array( 'ver' => 1, 'lastsync' => $lastsync ) );
    251                         $this->log( $synclist, 'ljimport-items-' . $total . '.txt' );
    252253                       
    253254                        // Keep track of if we've downloaded everything
    254255                        $total = $synclist['total'];
     
    261262                                                $lastsync = $event['time'];
    262263                                }
    263264                        }
    264 
    265                         update_option( 'ljapi_sync_item_times', $sync_item_times );
    266                         update_option( 'ljapi_total', $total );
    267                         update_option( 'ljapi_count', $count );
    268                         update_option( 'ljapi_lastsync', $lastsync );
    269265                } while ( $total > $count );
    270266                // endwhile - all post meta is cached locally
    271                 $this->log( $sync_item_times, 'ljimport-post-mod-times.txt' );
    272267               
     268                update_option( 'ljapi_sync_item_times', $sync_item_times );
     269                update_option( 'ljapi_total', $total );
     270                update_option( 'ljapi_count', $count );
     271                update_option( 'ljapi_lastsync', $lastsync );
     272
     273                echo '<p>' . __( 'Post metadata has been downloaded, proceeding with posts...' ) . '</p>';
     274               
    273275                echo '<ol>';
    274276               
    275277                $imported_count = (int) get_option( 'ljapi_imported_count' );
     
    285287                                                                                                                        'selecttype' => 'syncitems',
    286288                                                                                                                        'lineendings' => 'pc',
    287289                                                                                                                        'lastsync' => $lastsync ) );
    288                         $this->log( $itemlist, 'ljimport-posts-' . $imported_count . '.txt' );
    289290                        if ( is_wp_error( $itemlist ) )
    290291                                return $itemlist;
     292                               
    291293                        if ( $num = count( $itemlist['events'] ) ) {
    292294                                foreach ( $itemlist['events'] as $event ) {
    293295                                        $imported_count++;
     
    312314                        return;
    313315               
    314316                $user = wp_get_current_user();
    315                 $post_author   = $user->ID;
    316                 $post_status   = ( 'private' == trim( $post['security'] ) ) ? 'private' : 'publish'; // Only me
    317                 $post_password = ( 'usemask' == trim( $post['security'] ) ) ? $this->protected_password : ''; // "Friends" via password
     317                $post_author      = $user->ID;
     318                $post['security'] = !empty( $post['security'] ) ? $post['security'] : '';
     319                $post_status      = ( 'private' == trim( $post['security'] ) ) ? 'private' : 'publish'; // Only me
     320                $post_password    = ( 'usemask' == trim( $post['security'] ) ) ? $this->protected_password : ''; // "Friends" via password
    318321
    319322                // For some reason, LJ sometimes sends a date as "2004-04-1408:38:00" (no space btwn date/time)
    320323                $post_date = $post['eventtime'];
     
    322325                        $post_date = substr( $post_date, 0, 10 ) . ' ' . substr( $post_date, 10 );
    323326               
    324327                // Cleaning up and linking the title
    325                 $post_title = trim( $post['subject'] );
     328                $post_title = isset( $post['subject'] ) ? trim( $post['subject'] ) : '';
    326329                $post_title = $this->translate_lj_user( $post_title ); // Translate it, but then we'll strip the link
    327330                $post_title = strip_tags( $post_title ); // Can't have tags in the title in WP
    328331                $post_title = $wpdb->escape( $post_title );
     
    365368                        }
    366369                        $postdata['post_ID']   = $post_id;
    367370                        $postdata['lj_itemid'] = $post['itemid'];
    368                         $this->log( $postdata, 'ljimport-post-' . $post_id . '.txt' );
    369371                       
    370372                        // Handle all the metadata for this post
    371373                        $this->insert_postmeta( $post_id, $post );
     
    404406                }
    405407        }
    406408       
    407         // Loops through and gets comment meta and content from LJ in batches
    408         // Writes raw XML files to disk for later processing
    409         function download_comments() {
     409        // Set up a session (authenticate) with LJ
     410        function getsession() {
    410411                // Get a session via XMLRPC
    411412                $cookie = $this->lj_ixr( 'sessiongenerate', array( 'ver' => 1, 'expiration' => 'short' ) );
     413                return new WP_Http_Cookie( array( 'name' => 'ljsession', 'value' => $cookie['ljsession'] ) );
     414        }
     415       
     416        // Loops through and gets comment meta from LJ in batches
     417        function download_comment_meta() {
     418                $cookie = $this->getsession();
    412419               
    413                 // Comment Meta
    414                
    415420                // Load previous state (if any)
    416421                $this->usermap = (array) get_option( 'ljapi_usermap' );
    417                 $maxid         = (int) get_option( 'ljapi_maxid' ) || 1;
    418                 $highest_id    = (int) get_option( 'ljapi_highest_id' );
    419 
    420                 // Snoopy is required to handle the cookie
    421                 $this->snoop = new Snoopy();
    422                 $this->snoop->cookies = $cookie;
     422                $maxid         = get_option( 'ljapi_maxid' ) ? get_option( 'ljapi_maxid' ) : 1;
     423                $highest_id    = get_option( 'ljapi_highest_id' ) ? get_option( 'ljapi_highest_id' ) : 0;
    423424               
    424425                // We need to loop over the metadata request until we have it all
    425426                while ( $maxid > $highest_id ) {
    426427                        // Now get the meta listing
    427                         if ( !$this->snoop->fetch( $this->comments_url . '?get=comment_meta&startid=' . ( $highest_id + 1 ) ) )
    428                                 return new WP_Error( 'Snoopy', __( 'Failed to retrieve comment meta information from LiveJournal. Please try again soon.' ) );
    429 
    430                         // Snoopy doesn't provide an accessor for results...
    431                         $results = $this->snoop->results;
     428                        $results = wp_remote_get( $this->comments_url . '?get=comment_meta&startid=' . ( $highest_id + 1 ),
     429                                                                                array( 'cookies' => array( $cookie ), 'timeout' => 20 ) );
     430                        if ( is_wp_error( $results ) )
     431                                return new WP_Error( 'comment_meta', __( 'Failed to retrieve comment meta information from LiveJournal. Please try again soon.' ) );
    432432                       
     433                        $results = wp_remote_retrieve_body( $results );
     434                       
    433435                        // Get the maxid so we know if we have them all yet
    434436                        preg_match( '|<maxid>(\d+)</maxid>|', $results, $matches );
     437                        if ( 0 == $matches[1] ) {
     438                                // No comment meta = no comments for this journal
     439                                echo '<p>' . __( 'You have no comments to import!' ) . '</p>';
     440                                update_option( 'ljapi_highest_id', 1 );
     441                                update_option( 'ljapi_highest_comment_id', 1 );
     442                                return false; // Bail out of comment importing entirely
     443                        }
    435444                        $maxid = !empty( $matches[1] ) ? $matches[1] : $maxid;
    436445                       
    437446                        // Parse comments and get highest id available
     
    446455                        foreach ( $matches[1] as $count => $userid )
    447456                                $this->usermap[$userid] = $matches[2][$count]; // need this in memory for translating ids => names
    448457                               
    449                         update_option( 'ljapi_usermap',    $this->usermap );
    450                         update_option( 'ljapi_maxid',      $maxid );
    451                         update_option( 'ljapi_highest_id', $highest_id );
     458                        wp_cache_flush();
    452459                }
    453460                // endwhile - should have seen all comment meta at this point
     461
     462                update_option( 'ljapi_usermap',    $this->usermap );
     463                update_option( 'ljapi_maxid',      $maxid );
     464                update_option( 'ljapi_highest_id', $highest_id );
    454465               
     466                echo '<p>' . __( ' Comment metadata downloaded successfully, proceeding with posts...' ) . '</p>';
    455467               
    456                 // Download Comment XML
    457                
    458                 // Load previous state (if any)
    459                 $highest_id          = (int) get_option( 'ljapi_highest_comment_id' );
    460                 $comment_xml_files   = get_option( 'ljapi_comment_xml_files' );
    461                 if ( !is_array( $comment_xml_files ) ) {
    462                         update_option( 'ljapi_comment_xml_files', array() );
    463                         $comment_xml_files = array();
    464                 }
    465                
    466                 echo '<ol>';
    467                
    468                 // And now request the actual comments, and keep going until we have them all
    469                 while ( $maxid > $highest_id ) {
    470                         // Get a batch of comments, using the highest_id we've already got as a starting point
    471                         if ( !$this->snoop->fetch( $this->comments_url . '?get=comment_body&startid=' . ( $highest_id + 1 ) ) )
    472                                 return new WP_Error( 'Snoopy', __( 'Failed to retrieve comment bodies from LiveJournal. Please try again soon.' ) );
    473                        
    474                         // Get the highest post ID in this batch (required for loop control)
    475                         $results = $this->snoop->results;
    476                         preg_match_all( '|<comment id=\'(\d+)\'|i', $results, $comments );
    477                         for ( $r = 0; $r < count( $comments[1] ); $r++ ) {
    478                                 if ( $comments[1][$r] > $highest_id )
    479                                         $highest_id = $comments[1][$r];
    480                         }
    481                        
    482                         // $this->snoop-results is where the actual response is stored
    483                         $this->log( $this->snoop->results, 'ljimport-comment-bodies-' . $highest_id . '.txt' );
    484                        
    485                         // Store in uploads dir. Can't use *.xml because it's not allowed
    486                         $results = wp_upload_bits( 'raw-comments-' . $highest_id . '.txt', null, $results );
    487                         if ( !empty( $results['error'] ) )
    488                                 return new WP_Error( 'xml', $results['error'] );
    489                         $comment_xml_files[] = $results['file'];
    490                        
    491                         echo '<li>' . sprintf( __( 'Downloaded <strong>%s</strong>' ), basename( $results['file'] ) ) . '</li>';
    492                         ob_flush(); flush();
    493                        
    494                         $comment_xml_files = array_unique( $comment_xml_files );
    495                         update_option( 'ljapi_comment_xml_files', $comment_xml_files );
    496                         update_option( 'ljapi_comment_xml_files_count', count( $comment_xml_files ) );
    497                 }
    498                 // endwhile - all comments downloaded and ready for bulk processing
    499                
    500                 echo '</ol>';
    501                
    502468                return true;
    503469        }
    504470       
    505         function parse_comment_xml( $xml_file ) {
    506                 if ( !is_file( $xml_file ) || !is_readable( $xml_file ) )
    507                         return new WP_Error( 'file', sprintf( __( 'Could not access comment XML file: %s'), $filename ) );
    508                        
    509                 // Get content from file
    510                 $xml = @file_get_contents( $xml_file );
    511 
    512                 $cache_files = get_option( 'ljapi_comment_cache_files' );
    513                 if ( !is_array( $cache_files ) )
    514                         $cache_files = array();
     471        // Downloads actual comment bodies from LJ
     472        // Inserts them all directly to the DB, with additional info stored in "spare" fields
     473        function download_comment_bodies() {
     474                global $wpdb;
     475                $cookie = $this->getsession();
    515476               
    516                 // Parse XML into comments
    517                 preg_match_all( '|<comment id.*</comment>|iUs', $xml, $matches );
    518                 unset( $xml );
    519                 for ( $c = 0; $c < count( $matches[0] ); $c++ ) {
    520                         $comment = $matches[0][$c];
     477                // Load previous state (if any)
     478                $this->usermap = (array) get_option( 'ljapi_usermap' );
     479                $maxid         = get_option( 'ljapi_maxid' ) ? (int) get_option( 'ljapi_maxid' ) : 1;
     480                $highest_id    = (int) get_option( 'ljapi_highest_comment_id' );
     481                $loop = 0;
     482                while ( $maxid > $highest_id && $loop < 5 ) { // We do 5 loops per call to avoid memory limits
     483                        $loop++;
    521484                       
    522                         // Filter out any captured, deleted comments (nothing useful to import)
    523                         $comment = preg_replace( '|<comment id=\'\d+\' jitemid=\'\d+\' posterid=\'\d+\' state=\'D\'[^/]*/>|is', '', $comment );
     485                        // Get a batch of comments, using the highest_id we've already got as a starting point
     486                        $results = wp_remote_get( $this->comments_url . '?get=comment_body&startid=' . ( $highest_id + 1 ),
     487                                                                                array( 'cookies' => array( $cookie ), 'timeout' => 20 ) );
     488                        if ( is_wp_error( $results ) )
     489                                return new WP_Error( 'comment_bodies', __( 'Failed to retrieve comment bodies from LiveJournal. Please try again soon.' ) );
    524490                       
    525                         // Parse this comment into an array
    526                         $comment = $this->parse_comment( $comment );
    527                         if ( empty( $comment['comment_post_ID'] ) )
    528                                 continue;
     491                        $results = wp_remote_retrieve_body( $results );
    529492                       
    530                         // Add this comment to the appropriate cache file
    531                         $filename = $this->full_path( 'ljimport-comments-' . $comment['comment_post_ID'] . '.php' );
    532                         if ( $this->write_file( '<?php $comments[] = ' . var_export( $comment, true ) . '; ?>' . "\n",
    533                                                                 $filename,
    534                                                                 $comment['comment_post_ID'],
    535                                                                 'a' ) )
    536                         {
    537                                 // Keep track of files used
    538                                 $cache_files[] = $filename;
     493                        // Parse out each comment and insert directly
     494                        preg_match_all( '|<comment id=\'(\d+)\'.*</comment>|iUs', $results, $matches );
     495                        for ( $c = 0; $c < count( $matches[0] ); $c++ ) {
     496                                // Keep track of highest id seen
     497                                if ( $matches[1][$c] > $highest_id ) {
     498                                        $highest_id = $matches[1][$c];
     499                                        update_option( 'ljapi_highest_comment_id', $highest_id );
     500                                }
     501                                       
     502                                $comment = $matches[0][$c];
     503
     504                                // Filter out any captured, deleted comments (nothing useful to import)
     505                                $comment = preg_replace( '|<comment id=\'\d+\' jitemid=\'\d+\' posterid=\'\d+\' state=\'D\'[^/]*/>|is', '', $comment );
     506
     507                                // Parse this comment into an array and insert
     508                                $comment = $this->parse_comment( $comment );
     509                                $id = wp_insert_comment( $comment );
     510
     511                                // Clear cache
     512                                clean_comment_cache( $id );
    539513                        }
     514                       
     515                        // Clear cache to preseve memory
     516                        wp_cache_flush();
    540517                }
     518                // endwhile - all comments downloaded and ready for bulk processing
    541519               
    542                 // Update list of files in the DB
    543                 sort( $cache_files );
    544                 $cache_files = array_unique( $cache_files );
    545                 update_option( 'ljapi_comment_cache_files', $cache_files );
    546                 update_option( 'ljapi_comment_cache_files_count', count( $cache_files ) );
    547                 $this->close_file_pointers();
     520                // Counter just used to show progress to user
     521                update_option( 'ljapi_comment_batch', ( (int) get_option( 'ljapi_comment_batch' ) + 1 ) );
    548522               
    549                 // Don't need this XML file anymore
    550                 unlink( $xml_file );
    551                
    552523                return true;
    553524        }
    554525       
     526        // Takes a block of XML and parses out all the elements of the comment
    555527        function parse_comment( $comment ) {
    556528                global $wpdb;
    557529               
     
    562534                preg_match( '| jitemid=\'(\d+)\'|i', $attribs[1], $matches );
    563535                $lj_comment_post_ID = $matches[1];
    564536                preg_match( '| posterid=\'(\d+)\'|i', $attribs[1], $matches );
    565                 $comment_author_ID = $matches[1];
    566                 preg_match( '| parentid=\'(\d+)\'|i', $attribs[1], $matches );
    567                 $lj_comment_parent = $matches[1];
    568                 preg_match( '| state=\'([SDFA])\'|i', $attribs[1], $matches );
    569                 $lj_comment_state = !empty( $matches[1] ) ? $matches[1] : 'A';
     537                $comment_author_ID = isset( $matches[1] ) ? $matches[1] : 0;
     538                preg_match( '| parentid=\'(\d+)\'|i', $attribs[1], $matches ); // optional
     539                $lj_comment_parent = isset( $matches[1] ) ? $matches[1] : 0;
     540                preg_match( '| state=\'([SDFA])\'|i', $attribs[1], $matches ); // optional
     541                $lj_comment_state = isset( $matches[1] ) ? $matches[1] : 'A';
    570542               
    571543                // Clean up "subject" - this will become the first line of the comment in WP
    572544                preg_match( '|<subject>(.*)</subject>|is', $comment, $matches );
    573                 $comment_subject = $wpdb->escape( trim( $matches[1] ) );
    574                 if ( 'Re:' == $comment_subject )
    575                         $comment_subject = '';
     545                if ( isset( $matches[1] ) ) {
     546                        $comment_subject = $wpdb->escape( trim( $matches[1] ) );
     547                        if ( 'Re:' == $comment_subject )
     548                                $comment_subject = '';
     549                }
    576550               
    577551                // Get the body and HTMLize it
    578552                preg_match( '|<body>(.*)</body>|is', $comment, $matches );
     
    590564                $comment_date = trim( str_replace( array( 'T', 'Z' ), ' ', $matches[1] ) );
    591565               
    592566                // Grab IP if available
    593                 preg_match( '|<property name=\'poster_ip\'>(.*)</property>|i', $comment, $matches );
    594                 $comment_author_IP = $matches[1];
     567                preg_match( '|<property name=\'poster_ip\'>(.*)</property>|i', $comment, $matches ); // optional
     568                $comment_author_IP = isset( $matches[1] ) ? $matches[1] : '';
    595569               
    596570                // Try to get something useful for the comment author, especially if it was "my" comment
    597                 $author = ( substr( $this->usermap[$comment_author_ID], 0, 4 ) == 'ext_' || empty( $comment_author_ID ) ) ? __( 'Anonymous' ) : $this->usermap[$comment_author_ID];
     571                $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];
    598572                if ( get_option( 'ljapi_username' ) == $author ) {
    599573                        $user    = wp_get_current_user();
    600574                        $user_id = $user->ID;
     
    613587                                                'comment_post_ID' => $this->get_wp_post_ID( $lj_comment_post_ID ),
    614588                                                'comment_author' => $author,
    615589                                                'comment_author_url' => $url,
     590                                                'comment_author_email' => '',
    616591                                                'comment_content' => $comment_content,
    617592                                                'comment_date' => $comment_date,
    618593                                                'comment_author_IP' => ( !empty( $comment_author_IP ) ? $comment_author_IP : '' ),
    619594                                                'comment_approved' => ( in_array( $lj_comment_state, array( 'A', 'F' ) ) ? 1 : 0 ),
    620                                                 'comment_agent' => 'WP LJ Importer',
    621                                                 'user_id' => $user_id
    622                                                 );
     595                                                'comment_karma' => $lj_comment_ID, // Need this and next value until rethreading is done
     596                                                'comment_agent' => $lj_comment_parent,
     597                                                'comment_type' => 'livejournal',  // Custom type, so we can find it later for processing
     598                                                'user_ID' => $user_id
     599                                        );
    623600        }
    624601       
    625602       
     
    627604        function get_wp_post_ID( $post ) {
    628605                global $wpdb;
    629606                if ( empty( $this->postmap[$post] ) )
    630                         $this->postmap[$post] = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'lj_itemid' AND meta_value = %d", $post ) );
     607                        $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 ) );
    631608                return $this->postmap[$post];
    632609        }
    633610       
    634         // Re-build the threading within a single cache file
    635         function thread_comments( $filename ) {
    636                 if ( !is_file( $filename ) || !is_readable( $filename ) )
    637                         return new WP_Error( 'File', __( sprintf( 'Cannot access file %s', $filename ) ) );
    638                
    639                 $comments = array();
    640                 @include( $filename );
    641                 $this->comments = $comments;
    642                 unset( $comments );
    643                 if ( !is_array( $this->comments ) )
    644                         $this->comments = array();
    645                        
    646                 $count = count( $this->comments );
    647                 for ( $c = 0; $c < $count; $c++ ) {
    648                         // Skip anything that's not "top-level" for now
    649                         if ( 0 != $this->comments[$c]['lj_comment_parent'] )
    650                                 continue;
    651                         $this->comments[$c]['children'] = $this->get_child_comments( $this->comments[$c]['lj_comment_ID'] );
    652                 }
    653                
    654                 // Remove anything that's not supposed to be at top level
    655                 $top_comments = array();
    656                 for ( $c = 0; $c < $count; $c++ ) {
    657                         if ( 0 == $this->comments[$c]['lj_comment_parent'] ) {
    658                                 $top_comments[] = $this->comments[$c];
    659                         }
    660                 }
    661                
    662                 // Write back to file
    663                 @unlink( $filename );
    664                 $this->write_file( '<?php $comments = ' . var_export( $top_comments, true ) . '; ?>', $filename, $count, 'w' );
    665                 unset( $top_comments );
    666                 $this->close_file_pointers();
    667                
    668                 // Reference this file as being threaded
    669                 $files = get_option( 'ljapi_comment_threaded_files' );
    670                 $files[] = $filename;
    671                 array_unique( $files );
    672                 update_option( 'ljapi_comment_threaded_files', $files );
    673                 update_option( 'ljapi_comment_threaded_files_count', count( $files ) );
    674                
    675                 return true;
     611        // Gets the comment_ID that a LJ comment has been saved as within WP
     612        function get_wp_comment_ID( $comment ) {
     613                global $wpdb;
     614                if ( empty( $this->commentmap[$comment] ) )
     615                        $this->commentmap[$comment] = $wpdb->get_var( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_karma = %d", $comment ) );
     616                return $this->commentmap[$comment];
    676617        }
    677        
    678         function get_child_comments( $id ) {
    679                 $children = array();
    680                 $count = count( $this->comments );
    681                 for ( $c = 0; $c < $count; $c++ ) {
    682                         // This comment is a child of the $id
    683                         if ( $id == $this->comments[$c]['lj_comment_parent'] ) {
    684                                 $this->comments[$c]['children'] = $this->get_child_comments( $this->comments[$c]['lj_comment_ID'] );
    685                                 $children[] = $this->comments[$c];
    686                         }
    687                 }
    688                 return $children;
    689         }
    690        
    691         // Inserts the contents of each cache file (should be threaded already)
    692         function insert_comments( $filename ) {
    693                 echo '<ol>';
    694 
    695                 if ( !is_file( $filename ) || !is_readable( $filename ) )
    696                         return new WP_Error( 'File', __( sprintf( 'Cannot access file %s', $filename ) ) );
    697                
    698                 $comments = array();
    699                 @include( $filename );
    700                 $this->comments = $comments;
    701                 unset( $comments );
    702                 if ( !is_array( $this->comments ) )
    703                         $this->comments = array();
    704618                       
    705                 $count = count( $this->comments );
    706                 for ( $c = 0; $c < $count; $c++ ) {
    707                         $comment =& $this->comments[$c];
    708                         echo '<li>';
    709                         printf( __( 'Imported comment from <strong>%s</strong> on %s' ), $comment['comment_author'], $comment['comment_date'] );
    710 
    711                         $id = wp_insert_comment( $comment );
    712                         $comment['comment_ID'] = $id;
    713                         if ( count( $comment['children'] ) ) {
    714                                 _e( ' and replies:' );
    715                                 $this->insert_child_comments( $comment['children'], $id );
    716                         }
    717                        
    718                         echo '</li>';
    719                 }
    720                
    721                 // Remove the file now that we're done with it
    722                 @unlink( $filename );
    723 
    724                 echo '</ol>';
    725                
    726                 return true;
    727         }
    728        
    729         function insert_child_comments( &$comments, $parent ) {
    730                 echo '<ol>';
    731                 $count = count( $comments );
    732                 for ( $c = 0; $c < $count; $c++ ) {
    733                         $comment =& $comments[$c];
    734                         $comment['comment_parent'] = $parent;
    735                         echo '<li>';
    736                         printf( __( 'Imported reply from <strong>%s</strong> on %s' ), $comment['comment_author'], $comment['comment_date'] );
    737 
    738                         $id = wp_insert_comment( $comment );
    739                         $comment['comment_ID'] = $id;
    740                         if ( count( $comment['children'] ) ) {
    741                                 _e( ' and replies:' );
    742                                 $this->insert_child_comments( $comment['children'], $id );
    743                         }
    744                        
    745                         echo '</li>';
    746                 }
    747                 echo '</ol>';
    748         }
    749                        
    750619        function lj_ixr() {
    751620                if ( $challenge = $this->ixr->query( 'LJ.XMLRPC.getchallenge' ) ) {
    752621                        $challenge = $this->ixr->getResponse();
     
    767636                if ( $this->ixr->query( 'LJ.XMLRPC.' . $method, $params ) ) {
    768637                        return $this->ixr->getResponse();
    769638                } else {
    770                         $this->log( $this->ixr->message, 'ljimport-error-' . $method . '.txt' );
    771639                        return new WP_Error( 'IXR', __( 'XML-RPC Request Failed - ' ) . $this->ixr->getErrorCode() . ': ' . $this->ixr->getErrorMessage() );
    772640                }
    773641        }
     
    788656                                $this->greet();
    789657                                break;
    790658                        case 1 :
    791                         case 2 :
    792659                                $this->ixr = new IXR_Client( $this->ixr_url );
    793660                                // Intentional no break
     661                        case 2 :
    794662                        case 3 :
    795                         case 4 :
    796                         case 5 :
    797663                                check_admin_referer( 'lj-api-import' );
    798664                                $result = $this->{ 'step' . $step }();
    799665                                if ( is_wp_error( $result ) )
     
    837703                $login = $this->lj_ixr( 'login' );
    838704                if ( is_wp_error( $login ) ) {
    839705                        if ( 100 == $this->ixr->getErrorCode() || 101 == $this->ixr->getErrorCode() ) {
     706                                delete_option( 'ljapi_username' );
     707                                delete_option( 'ljapi_password' );
    840708                                ?>
    841709                                <p><?php _e( 'Logging in to LiveJournal failed. Check your username and password and try again.' ) ?></p>
    842710                                <p><a href="<?php echo $_SERVER['PHP_SELF'] . '?import=livejournal&amp;step=-1&amp;_wpnonce=' . wp_create_nonce( 'lj-api-import' ) . '&amp;_wp_http_referer=' . attribute_escape( str_replace( '&step=1', '', $_SERVER['REQUEST_URI'] ) ) ?>"><?php _e( 'Start again' ) ?></a></p>
     
    848716                }
    849717               
    850718                // Set up some options to avoid them autoloading (these ones get big)
    851                 add_option( 'ljapi_sync_item_times',        '', '', 'no' );
    852                 add_option( 'ljapi_usermap',                '', '', 'no' );
    853                 add_option( 'ljapi_comment_xml_files',      '', '', 'no' );
    854                 add_option( 'ljapi_comment_cache_files',    '', '', 'no' );
    855                 add_option( 'ljapi_comment_threaded_files', '', '', 'no' );
     719                add_option( 'ljapi_sync_item_times',  '', '', 'no' );
     720                add_option( 'ljapi_usermap',          '', '', 'no' );
     721                update_option( 'ljapi_comment_batch', 0 );
    856722               
    857723                echo '<h3>' . __( 'Importing Posts' ) . '</h3>';
    858724                echo '<p>' . __( "We're downloading and importing all your LiveJournal posts..." ) . '</p>';
     
    873739                        }
    874740                }
    875741               
    876                 echo '<p>' . __( "Your posts have all been imported, but wait - there's more! Now we need to process &amp; import your comments." ) . '</p>';
     742                echo '<p>' . __( "Your posts have all been imported, but wait - there's more! Now we need to download &amp; import your comments." ) . '</p>';
    877743                echo $this->next_step( 2, __( 'Download my comments &raquo;' ) );
    878744                $this->auto_submit();
    879745        }
     
    884750                update_option( 'ljapi_step', 2 );
    885751                $this->username = get_option( 'ljapi_username' );
    886752                $this->password = get_option( 'ljapi_password' );
     753                $this->ixr = new IXR_Client( $this->ixr_url );
    887754               
     755                echo '<div id="ljapi-status">';
    888756                echo '<h3>' . __( 'Downloading Comments' ) . '</h3>';
    889                 echo '<p>' . __( 'Now we will download your comments so we can process and import them...' ) . '</p>';
     757                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>';
    890758                ob_flush(); flush();
    891759               
    892                 $result = $this->download_comments();
    893                 if ( is_wp_error( $result ) )
    894                         return $result;
    895 
    896                 echo '<p>' . __( 'Your comments have all been downloaded to this server now, so we can process them and get them ready for importing.' ) . '</p>';
    897                 echo $this->next_step( 3, __( 'Process my comment files &raquo;' ) );
    898                 $this->auto_submit();
    899         }
    900 
    901         // Parse XML into comment cache files   
    902         function step3() {
    903 
    904                 set_time_limit( 0 );
    905                 update_option( 'ljapi_step', 3 );
    906                
    907                 $this->usermap = get_option( 'ljapi_usermap' );
    908 
    909                 echo '<div id="ljapi-status">';
    910                 echo '<h3>' . __( 'Parsing Comments' ) . '</h3>';
    911                 echo '<p>' . __( 'Time to clean up your comments and get them into a format WordPress understands...' ) . '</p>';
    912                 ob_flush(); flush();
    913                
    914                 $files = get_option( 'ljapi_comment_xml_files' );
    915                 if ( count( $files ) ) {
    916                         $file = array_pop( $files );
    917                
    918                         $result = $this->parse_comment_xml( $file );
     760                if ( !get_option( 'ljapi_usermap' ) ) {
     761                        // We haven't downloaded meta yet, so do that first
     762                        $result = $this->download_comment_meta();
    919763                        if ( is_wp_error( $result ) )
    920764                                return $result;
    921 
    922                         update_option( 'ljapi_comment_xml_files', $files );
    923765                }
    924                
    925                 if ( count( $files ) ) {
    926                         ?>
    927                                 <form action="admin.php?import=livejournal" method="post" id="ljapi-auto-repost">
    928                                 <p><strong><?php printf( __( 'Processed comment file %d of %d' ), ( get_option( 'ljapi_comment_xml_files_count' ) - count( $files ) ), get_option( 'ljapi_comment_xml_files_count' ) ) ?></strong></p>
    929                                 <?php wp_nonce_field( 'lj-api-import' ) ?>
    930                                 <input type="hidden" name="step" id="step" value="3" />
    931                                 <p><input type="submit" class="button-primary" value="<?php echo attribute_escape( __( 'Process the next comment file &raquo;' ) ) ?>" /> <span id="auto-message"></span></p>
    932                                 </form>
    933                                 <?php $this->auto_ajax( 'ljapi-auto-repost', 'auto-message', 0 ); ?>
    934                         <?php
     766
     767                // Download a batch of actual comments
     768                $result = $this->download_comment_bodies();
     769                if ( is_wp_error( $result ) )
     770                        return $result;
     771
     772                $maxid      = get_option( 'ljapi_maxid' ) ? (int) get_option( 'ljapi_maxid' ) : 1;
     773                $highest_id = (int) get_option( 'ljapi_highest_comment_id' );
     774                if ( $maxid > $highest_id ) {
     775                ?>
     776                        <form action="admin.php?import=livejournal" method="post" id="ljapi-auto-repost">
     777                        <p><strong><?php printf( __( 'Imported comment batch %d of <strong>approximately</strong> %d' ), get_option( 'ljapi_comment_batch' ), ( $maxid / 5000 ) ) ?></strong></p>
     778                        <?php wp_nonce_field( 'lj-api-import' ) ?>
     779                        <input type="hidden" name="step" id="step" value="2" />
     780                        <p><input type="submit" class="button-primary" value="<?php echo attribute_escape( __( 'Import the next batch &raquo;' ) ) ?>" /> <span id="auto-message"></span></p>
     781                        </form>
     782                        <?php $this->auto_ajax( 'ljapi-auto-repost', 'auto-message', 0 ); ?>
     783                <?php
    935784                } else {
    936                         echo '<p>' . __( 'Yay, we finished processing all of your comment files! Now we need to re-build your conversation threads.' ) . '</p>';
    937                         echo $this->next_step( 4, __( 'Thread my comments &raquo;' ) );
     785                        echo '<p>' . __( 'Your comments have all been imported now, but we still need to rebuild your conversation threads.' ) . '</p>';
     786                        echo $this->next_step( 3, __( 'Rebuild my comment threads &raquo;' ) );
    938787                        $this->auto_submit();
    939788                }
    940789                echo '</div>';
    941790        }
    942 
    943         // Thread comments within their cache files     
    944         function step4() {
     791       
     792        // Re-thread comments already in the DB
     793        function step3() {
     794                global $wpdb;
    945795                set_time_limit( 0 );
    946                 update_option( 'ljapi_step', 4 );
     796                update_option( 'ljapi_step', 3 );
    947797               
    948798                echo '<div id="ljapi-status">';
    949799                echo '<h3>' . __( 'Threading Comments' ) . '</h3>';
    950                 echo '<p>' . __( 'Re-building your conversation threads ready for import...' ) . '</p>';
     800                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>';
    951801                ob_flush(); flush();
    952802               
    953                 $files = get_option( 'ljapi_comment_cache_files' );
    954                 if ( count( $files ) ) {
    955                         $file = array_pop( $files );
    956                
    957                         $result = $this->thread_comments( $file );
    958                         if ( is_wp_error( $result ) )
    959                                 return $result;
    960                        
    961                         update_option( 'ljapi_comment_cache_files', $files );
     803                // Only bother adding indexes if they have over 5000 comments (arbitrary number)
     804                $imported_comments = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_type = 'livejournal'" );
     805                $added_indices = false;
     806                if ( 5000 < $imported_comments ) {
     807                        include_once(ABSPATH . 'wp-admin/includes/upgrade.php');
     808                        $added_indices = true;
     809                        add_clean_index( $wpdb->comments, 'comment_type'  );
     810                        add_clean_index( $wpdb->comments, 'comment_karma' );
     811                        add_clean_index( $wpdb->comments, 'comment_agent' );
    962812                }
    963813               
    964                 if ( count( $files ) ) {
    965                         ?>
    966                                 <form action="admin.php?import=livejournal" method="post" id="ljapi-auto-repost">
    967                                 <p><strong><?php printf( __( 'Threaded cache file %d of %d' ), ( get_option( 'ljapi_comment_cache_files_count' ) - count( $files ) ), get_option( 'ljapi_comment_cache_files_count' ) ) ?></strong></p>
    968                                 <?php wp_nonce_field( 'lj-api-import' ) ?>
    969                                 <input type="hidden" name="step" id="step" value="4" />
    970                                 <p><input type="submit" class="button-primary" value="<?php echo attribute_escape( __( 'Thread the next cache file &raquo;' ) ) ?>" /> <span id="auto-message"></span></p>
    971                                 </form>
    972                                 <?php $this->auto_ajax( 'ljapi-auto-repost', 'auto-message', 0 ); ?>
    973                         <?php
    974                 } else {
    975                         echo '<p>' . __( "Alrighty, your comments are all threaded. There's just one last step -- time to actually import them all now!" ) . '</p>';
    976                         echo '<p>' . __( 'This last part in particular can take a really long time if you have a lot of comments. You might want to go and do something else while you wait.' ) . '</p>';
    977                         echo $this->next_step( 5, __( 'Import my threaded comments into WordPress &raquo;' ) );
    978                         $this->auto_submit();
     814                // Get LJ comments, which haven't been threaded yet, 5000 at a time and thread them
     815                while ( $comments = $wpdb->get_results( "SELECT comment_ID, comment_agent FROM {$wpdb->comments} WHERE comment_type = 'livejournal' AND comment_agent != '0' LIMIT 5000", OBJECT ) ) {
     816                        foreach ( $comments as $comment ) {
     817                                $wpdb->update( $wpdb->comments,
     818                                                                array( 'comment_parent' => $this->get_wp_comment_ID( $comment->comment_agent ), 'comment_type' => 'livejournal-done' ),
     819                                                                array( 'comment_ID' => $comment->comment_ID ) );
     820                        }
     821                        wp_cache_flush();
     822                        $wpdb->flush();
    979823                }
    980                 echo '</div>';
    981         }
    982 
    983         // Import comments from cache files into WP
    984         function step5() {
    985                 set_time_limit( 0 );
    986                 update_option( 'ljapi_step', 5 );
    987824               
    988                
    989                 echo '<div id="ljapi-status">';
    990                 echo '<h3>' . __( 'Importing Comments' ) . '</h3>';
    991                 echo '<p>' . __( 'This is the big one -- we are now inserting your comment threads into WordPress...' ) . '</p>';
    992                
    993                 $files = get_option( 'ljapi_comment_threaded_files' );
    994                 echo '<p><strong>' . sprintf( __( 'Importing cache file %d of %d' ), ( get_option( 'ljapi_comment_threaded_files_count' ) - count( $files ) + 1 ), get_option( 'ljapi_comment_threaded_files_count' ) ) . '</strong></p>';
    995                 ob_flush(); flush();
    996                
    997                 if ( count( $files ) ) {
    998                         $file = array_pop( $files );
    999                
    1000                         $result = $this->insert_comments( $file );
    1001                         if ( is_wp_error( $result ) )
    1002                                 return $result;
    1003                        
    1004                         update_option( 'ljapi_comment_threaded_files', $files );
     825                // Revert the comments table back to normal and optimize it to reclaim space
     826                if ( $added_indices ) {
     827                        drop_index( $wpdb->comments, 'comment_type'  );
     828                        drop_index( $wpdb->comments, 'comment_karma' );
     829                        drop_index( $wpdb->comments, 'comment_agent' );
     830                        $wpdb->query( "OPTIMIZE TABLE {$wpdb->comments}" );
    1005831                }
    1006832               
    1007                 if ( count( $files ) ) {
    1008                         ?>
    1009                                 <form action="admin.php?import=livejournal" method="post" id="ljapi-auto-repost">
    1010                                 <?php wp_nonce_field( 'lj-api-import' ) ?>
    1011                                 <input type="hidden" name="step" id="step" value="5" />
    1012                                 <p><input type="submit" class="button-primary" value="<?php echo attribute_escape( __( 'Import the next cache file &raquo;' ) ) ?>" /> <span id="auto-message"></span></p>
    1013                                 </form>
    1014                                 <?php $this->auto_ajax( 'ljapi-auto-repost', 'auto-message', 0 ); ?>
    1015                         <?php
    1016                 } else {
    1017                         // Clean up database and we're out
    1018                         $this->cleanup();
    1019                         do_action( 'import_done', 'livejournal' );
    1020                         echo '<h3>';
    1021                         printf( __( 'All done. <a href="%s">Have fun!</a>' ), get_option( 'home' ) );
    1022                         echo '</h3>';
    1023                 }
     833                // Clean up database and we're out
     834                $this->cleanup();
     835                do_action( 'import_done', 'livejournal' );
     836                if ( $imported_comments > 1 )
     837                        echo '<p>' . sprintf( __( "Successfully re-threaded %d comments." ), number_format( $imported_comments ) ) . '</p>';
     838                echo '<h3>';
     839                printf( __( 'All done. <a href="%s">Have fun!</a>' ), get_option( 'home' ) );
     840                echo '</h3>';
    1024841                echo '</div>';
    1025842        }
    1026843       
     
    1035852               
    1036853                return $str;
    1037854        }
    1038 
    1039         // Automatically submit the form with #id to continue the process
    1040         // Hide any submit buttons to avoid people clicking them
    1041         // Display a countdown in the element indicated by $msg for "Continuing in x"
    1042         function auto_ajax( $id = 'ljapi-next-form', $msg = 'auto-message', $seconds = 5 ) {
     855       
     856        // Automatically submit the specified form after $seconds
     857        // Include a friendly countdown in the element with id=$msg
     858        function auto_submit( $id = 'ljapi-next-form', $msg = 'auto-message', $seconds = 10 ) {
    1043859                ?><script type="text/javascript">
    1044860                        next_counter = <?php echo $seconds ?>;
    1045861                        jQuery(document).ready(function(){
     
    1052868                                if ( next_counter <= 0 ) {
    1053869                                        if ( jQuery( '#<?php echo $id ?>' ).length ) {
    1054870                                                jQuery( "#<?php echo $id ?> input[type='submit']" ).hide();
    1055                                                 jQuery.ajaxSetup({'timeout':3600000});
    1056                                                 str = '<?php _e( "Processing next file." ) ?> <img src="images/loading-publish.gif" alt="" id="processing" align="top" />';
     871                                                str = '<?php _e( "Continuing" ) ?> <img src="images/loading-publish.gif" alt="" id="processing" align="top" />';
    1057872                                                jQuery( '#<?php echo $msg ?>' ).html( str );
    1058                                                 jQuery('#ljapi-status').load(ajaxurl, {'action':'lj-importer',
    1059                                                                                                                                 'step':jQuery('#step').val(),
    1060                                                                                                                                 '_wpnonce':'<?php echo wp_create_nonce( 'lj-api-import' ) ?>',
    1061                                                                                                                                 '_wp_http_referer':'<?php echo $_SERVER['REQUEST_URI'] ?>'});
     873                                                jQuery( '#<?php echo $id ?>' ).submit();
    1062874                                                return;
    1063875                                        }
    1064876                                }
     
    1068880                </script><?php
    1069881        }
    1070882       
    1071         // Automatically submit the specified form after $seconds
    1072         // Include a friendly countdown in the element with id=$msg
    1073         function auto_submit( $id = 'ljapi-next-form', $msg = 'auto-message', $seconds = 10 ) {
     883        // Automatically submit the form with #id to continue the process
     884        // Hide any submit buttons to avoid people clicking them
     885        // Display a countdown in the element indicated by $msg for "Continuing in x"
     886        function auto_ajax( $id = 'ljapi-next-form', $msg = 'auto-message', $seconds = 5 ) {
    1074887                ?><script type="text/javascript">
    1075888                        next_counter = <?php echo $seconds ?>;
    1076889                        jQuery(document).ready(function(){
     
    1083896                                if ( next_counter <= 0 ) {
    1084897                                        if ( jQuery( '#<?php echo $id ?>' ).length ) {
    1085898                                                jQuery( "#<?php echo $id ?> input[type='submit']" ).hide();
    1086                                                 str = '<?php _e( "Continuing" ) ?> <img src="images/loading-publish.gif" alt="" id="processing" align="top" />';
     899                                                jQuery.ajaxSetup({'timeout':3600000});
     900                                                str = '<?php _e( "Processing next batch." ) ?> <img src="images/loading-publish.gif" alt="" id="processing" align="top" />';
    1087901                                                jQuery( '#<?php echo $msg ?>' ).html( str );
    1088                                                 jQuery( '#<?php echo $id ?>' ).submit();
     902                                                jQuery('#ljapi-status').load(ajaxurl, {'action':'lj-importer',
     903                                                                                                                                'step':jQuery('#step').val(),
     904                                                                                                                                '_wpnonce':'<?php echo wp_create_nonce( 'lj-api-import' ) ?>',
     905                                                                                                                                '_wp_http_referer':'<?php echo $_SERVER['REQUEST_URI'] ?>'});
    1089906                                                return;
    1090907                                        }
    1091908                                }
     
    1095912                </script><?php
    1096913        }
    1097914
    1098         // Remove all options used during import process
     915        // Remove all options used during import process and
     916        // set wp_comments entries back to "normal" values
    1099917        function cleanup() {
     918                global $wpdb;
     919               
    1100920                delete_option( 'ljapi_username' );
    1101921                delete_option( 'ljapi_password' );
    1102922                delete_option( 'ljapi_protected_password' );
     
    1111931                delete_option( 'ljapi_usermap' );
    1112932                delete_option( 'ljapi_highest_id' );
    1113933                delete_option( 'ljapi_highest_comment_id' );
    1114                 delete_option( 'ljapi_comment_xml_files' );
    1115                 delete_option( 'ljapi_comment_xml_files_count' );
    1116                 delete_option( 'ljapi_comment_cache_files' );
    1117                 delete_option( 'ljapi_comment_cache_files_count' );
    1118                 delete_option( 'ljapi_comment_threaded_files' );
    1119                 delete_option( 'ljapi_comment_threaded_files_count' );
     934                delete_option( 'ljapi_comment_batch' );
    1120935                delete_option( 'ljapi_step' );
     936               
     937                $wpdb->update( $wpdb->comments,
     938                                                array( 'comment_karma' => 0, 'comment_agent' => 'WP LJ Importer', 'comment_type' => '' ),
     939                                                array( 'comment_type' => 'livejournal-done' ) );
     940                $wpdb->update( $wpdb->comments,
     941                                                array( 'comment_karma' => 0, 'comment_agent' => 'WP LJ Importer', 'comment_type' => '' ),
     942                                                array( 'comment_type' => 'livejournal' ) );
    1121943        }
    1122944       
    1123         // Dump a string to a log file (appends to existing file)
    1124         function log( $string, $name ) {
    1125                 return; // remove this to enable "debugging" output to files in /wp-content/ljimport
    1126                 $path = wp_upload_dir();
    1127                 $path = $path['path'];
    1128                 if ( get_option( 'uploads_use_yearmonth_folders' ) )
    1129                         $path = substr( $path, 0, -8 );
    1130 
    1131                 if ( !is_dir( $path . '/ljimport' ) )
    1132                         mkdir( $path . '/ljimport' );
    1133                        
    1134                 $fh = @fopen( $path . '/ljimport/' . $name, 'a' );
    1135                 if ( $fh ) {
    1136                         if ( is_array( $string ) || is_object( $string ) )
    1137                                 fwrite( $fh, var_export( $string, true ) . "\n\n" );
    1138                         else
    1139                                 fwrite( $fh, $string . "\n\n" );
    1140                         fclose( $fh );
    1141                 }
    1142         }
    1143        
    1144         function write_file( $data, $name, $id, $mode = 'a' ) {
    1145                 if ( empty( $this->pointers[$id] ) )
    1146                         $this->pointers[$id] = @fopen( $name, $mode );
    1147                 if ( $this->pointers[$id] )
    1148                         return fwrite( $this->pointers[$id], $data );
    1149                 return false;
    1150         }
    1151        
    1152         function full_path( $basename ) {
    1153                 $uploads = wp_upload_dir();
    1154                 return $uploads['path'] . '/' . $basename;
    1155         }
    1156        
    1157         function close_file_pointers() {
    1158                 foreach ( $this->pointers as $p )
    1159                         @fclose( $p );
    1160         }
    1161 
    1162945        function LJ_API_Import() {
    1163946                $this->__construct();
    1164947        }