Make WordPress Core

Changeset 10501


Ignore:
Timestamp:
02/04/2009 06:24:02 PM (16 years ago)
Author:
ryan
Message:

Revert the revert per Matt. see #8999

Location:
trunk/wp-admin
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-admin/admin-ajax.php

    r10500 r10501  
    11591159
    11601160    break;
     1161case 'lj-importer' :
     1162    check_ajax_referer( 'lj-api-import' );
     1163    if ( !current_user_can( 'publish_posts' ) )
     1164        die('-1');
     1165    if ( empty( $_POST['step'] ) )
     1166        die( '-1' );
     1167
     1168    include( ABSPATH . 'wp-admin/import/livejournal.php' );
     1169    $result = $lj_api_import->{ 'step' . ( (int) $_POST['step'] ) }();
     1170    if ( is_wp_error( $result ) )
     1171        echo $result->get_error_message();
     1172    die;
     1173    break;
    11611174default :
    11621175    do_action( 'wp_ajax_' . $_POST['action'] );
  • trunk/wp-admin/import/livejournal.php

    r10499 r10501  
    11<?php
    22/**
    3  * LiveJournal Importer
     3 * LiveJournal API Importer
    44 *
    55 * @package WordPress
     
    77 */
    88
     9// XML-RPC library for communicating with LiveJournal API
     10require_once( ABSPATH . WPINC . '/class-IXR.php' );
     11
     12// Snoopy for getting comments (with cookies)
     13require_once( ABSPATH . WPINC . '/class-snoopy.php' );
     14
    915/**
    10  * LiveJournal Importer class
     16 * LiveJournal API Importer class
    1117 *
    12  * Imports your LiveJournal XML exported file into WordPress.
     18 * Imports your LiveJournal contents into WordPress using the LJ API
    1319 *
    14  * @since unknown
     20 * @since 2.7.1
    1521 */
    16 class LJ_Import {
    17 
    18     var $file;
     22class LJ_API_Import {
     23
     24    var $comments_url = 'http://www.livejournal.com/export_comments.bml';
     25    var $ixr_url      = 'http://www.livejournal.com/interface/xmlrpc';
     26    var $ixr;
     27    var $username;
     28    var $password;
     29    var $snoop;
     30    var $comment_meta;
     31    var $comments;
     32    var $usermap;
     33    var $postmap;
     34    var $pointers = array();
     35   
     36    // This list taken from LJ, they don't appear to have an API for it
     37    var $moods = array( '1' => 'aggravated',
     38                        '10' => 'discontent',
     39                        '100' => 'rushed',
     40                        '101' => 'contemplative',
     41                        '102' => 'nerdy',
     42                        '103' => 'geeky',
     43                        '104' => 'cynical',
     44                        '105' => 'quixotic',
     45                        '106' => 'crazy',
     46                        '107' => 'creative',
     47                        '108' => 'artistic',
     48                        '109' => 'pleased',
     49                        '11' => 'energetic',
     50                        '110' => 'bitchy',
     51                        '111' => 'guilty',
     52                        '112' => 'irritated',
     53                        '113' => 'blank',
     54                        '114' => 'apathetic',
     55                        '115' => 'dorky',
     56                        '116' => 'impressed',
     57                        '117' => 'naughty',
     58                        '118' => 'predatory',
     59                        '119' => 'dirty',
     60                        '12' => 'enraged',
     61                        '120' => 'giddy',
     62                        '121' => 'surprised',
     63                        '122' => 'shocked',
     64                        '123' => 'rejected',
     65                        '124' => 'numb',
     66                        '125' => 'cheerful',
     67                        '126' => 'good',
     68                        '127' => 'distressed',
     69                        '128' => 'intimidated',
     70                        '129' => 'crushed',
     71                        '13' => 'enthralled',
     72                        '130' => 'devious',
     73                        '131' => 'thankful',
     74                        '132' => 'grateful',
     75                        '133' => 'jealous',
     76                        '134' => 'nervous',
     77                        '14' => 'exhausted',
     78                        '15' => 'happy',
     79                        '16' => 'high',
     80                        '17' => 'horny',
     81                        '18' => 'hungry',
     82                        '19' => 'infuriated',
     83                        '2' => 'angry',
     84                        '20' => 'irate',
     85                        '21' => 'jubilant',
     86                        '22' => 'lonely',
     87                        '23' => 'moody',
     88                        '24' => 'pissed off',
     89                        '25' => 'sad',
     90                        '26' => 'satisfied',
     91                        '27' => 'sore',
     92                        '28' => 'stressed',
     93                        '29' => 'thirsty',
     94                        '3' => 'annoyed',
     95                        '30' => 'thoughtful',
     96                        '31' => 'tired',
     97                        '32' => 'touched',
     98                        '33' => 'lazy',
     99                        '34' => 'drunk',
     100                        '35' => 'ditzy',
     101                        '36' => 'mischievous',
     102                        '37' => 'morose',
     103                        '38' => 'gloomy',
     104                        '39' => 'melancholy',
     105                        '4' => 'anxious',
     106                        '40' => 'drained',
     107                        '41' => 'excited',
     108                        '42' => 'relieved',
     109                        '43' => 'hopeful',
     110                        '44' => 'amused',
     111                        '45' => 'determined',
     112                        '46' => 'scared',
     113                        '47' => 'frustrated',
     114                        '48' => 'indescribable',
     115                        '49' => 'sleepy',
     116                        '5' => 'bored',
     117                        '51' => 'groggy',
     118                        '52' => 'hyper',
     119                        '53' => 'relaxed',
     120                        '54' => 'restless',
     121                        '55' => 'disappointed',
     122                        '56' => 'curious',
     123                        '57' => 'mellow',
     124                        '58' => 'peaceful',
     125                        '59' => 'bouncy',
     126                        '6' => 'confused',
     127                        '60' => 'nostalgic',
     128                        '61' => 'okay',
     129                        '62' => 'rejuvenated',
     130                        '63' => 'complacent',
     131                        '64' => 'content',
     132                        '65' => 'indifferent',
     133                        '66' => 'silly',
     134                        '67' => 'flirty',
     135                        '68' => 'calm',
     136                        '69' => 'refreshed',
     137                        '7' => 'crappy',
     138                        '70' => 'optimistic',
     139                        '71' => 'pessimistic',
     140                        '72' => 'giggly',
     141                        '73' => 'pensive',
     142                        '74' => 'uncomfortable',
     143                        '75' => 'lethargic',
     144                        '76' => 'listless',
     145                        '77' => 'recumbent',
     146                        '78' => 'exanimate',
     147                        '79' => 'embarrassed',
     148                        '8' => 'cranky',
     149                        '80' => 'envious',
     150                        '81' => 'sympathetic',
     151                        '82' => 'sick',
     152                        '83' => 'hot',
     153                        '84' => 'cold',
     154                        '85' => 'worried',
     155                        '86' => 'loved',
     156                        '87' => 'awake',
     157                        '88' => 'working',
     158                        '89' => 'productive',
     159                        '9' => 'depressed',
     160                        '90' => 'accomplished',
     161                        '91' => 'busy',
     162                        '92' => 'blah',
     163                        '93' => 'full',
     164                        '95' => 'grumpy',
     165                        '96' => 'weird',
     166                        '97' => 'nauseated',
     167                        '98' => 'ecstatic',
     168                        '99' => 'chipper' );
    19169
    20170    function header() {
    21171        echo '<div class="wrap">';
    22172        screen_icon();
    23         echo '<h2>'.__('Import LiveJournal').'</h2>';
     173        echo '<h2>' . __( 'Import LiveJournal' ) . '</h2>';
    24174    }
    25175
     
    28178    }
    29179
    30     function unhtmlentities($string) { // From php.net for < 4.3 compat
    31         $trans_tbl = get_html_translation_table(HTML_ENTITIES);
    32         $trans_tbl = array_flip($trans_tbl);
    33         return strtr($string, $trans_tbl);
    34     }
    35 
    36180    function greet() {
    37         echo '<div class="narrow">';
    38         echo '<p>'.__('Howdy! Upload your LiveJournal XML export file and we&#8217;ll import the posts into this blog.').'</p>';
    39         echo '<p>'.__('Choose a LiveJournal XML file to upload, then click Upload file and import.').'</p>';
    40         wp_import_upload_form("admin.php?import=livejournal&amp;step=1");
    41         echo '</div>';
    42     }
    43 
     181        ?>
     182        <div class="narrow">
     183        <form action="admin.php?import=livejournal" method="post">
     184        <?php wp_nonce_field( 'lj-api-import' ) ?>
     185        <?php if ( get_option( 'ljapi_username' ) && get_option( 'ljapi_password' ) ) : ?>
     186            <input type="hidden" name="step" value="<?php echo get_option( 'ljapi_step' ) ?>" />
     187            <p><?php _e( 'It looks like you attempted to import your LiveJournal posts previously and got interrupted.' ) ?></p>
     188            <p class="submit">
     189                <input type="submit" class="button-primary" value="<?php echo attribute_escape( __( 'Continue previous import' ) ) ?>" />
     190            </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>
     192            <p>
     193        <?php else : ?>
     194            <input type="hidden" name="step" value="1" />
     195            <p><?php _e( 'Howdy! This importer allows you to connect directly to LiveJournal and download all your entries and comments' ) ?></p>
     196            <p><?php _e( 'Enter your LiveJournal username and password below so we can connect to your account:' ) ?></p>
     197       
     198            <table class="form-table">
     199
     200            <tr>
     201            <th scope="row"><label for="lj_username"><?php _e( 'LiveJournal Username' ) ?></label></th>
     202            <td><input type="text" name="lj_username" id="lj_username" class="regular-text" /></td>
     203            </tr>
     204
     205            <tr>
     206            <th scope="row"><label for="lj_password"><?php _e( 'LiveJournal Password' ) ?></label></th>
     207            <td><input type="password" name="lj_password" id="lj_password" class="regular-text" /></td>
     208            </tr>
     209     
     210            </table>
     211     
     212            <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>
     213            <p><?php _e( "If you don't enter a password, ALL ENTRIES from your LiveJournal will be imported as public posts in WordPress." ) ?></p>
     214            <p><?php _e( 'Enter the password you would like to use for all protected entries here:' ) ?></p>
     215            <table class="form-table">
     216
     217            <tr>
     218            <th scope="row"><label for="protected_password"><?php _e( 'Protected Post Password' ) ?></label></th>
     219            <td><input type="text" name="protected_password" id="protected_password" class="regular-text" /></td>
     220            </tr>
     221
     222            </table>
     223
     224            <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>
     225       
     226            <p class="submit">
     227                <input type="submit" class="button-primary" value="<?php echo attribute_escape( __( 'Connect to LiveJournal and Import' ) ) ?>" />
     228            </p>
     229       
     230            <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>
     231        <?php endif; ?>
     232        </form>
     233        </div>
     234        <?php
     235    }
     236   
    44237    function import_posts() {
    45         global $wpdb, $current_user;
    46 
    47         set_magic_quotes_runtime(0);
    48         $importdata = file($this->file); // Read the file into an array
    49         $importdata = implode('', $importdata); // squish it
    50         $importdata = str_replace(array ("\r\n", "\r"), "\n", $importdata);
    51 
    52         preg_match_all('|<entry>(.*?)</entry>|is', $importdata, $posts);
    53         $posts = $posts[1];
    54         unset($importdata);
     238        $total           = (int) get_option( 'ljapi_total' );
     239        $count           = (int) get_option( 'ljapi_count' );
     240        $lastsync        = get_option( 'ljapi_lastsync' );
     241        if ( !$lastsync ) {
     242            update_option( 'ljapi_lastsync', '1900-01-01 00:00:00' );
     243        }
     244        $sync_item_times = get_option( 'ljapi_sync_item_times' );
     245        if ( !is_array( $sync_item_times ) )
     246            $sync_item_times = array();
     247       
     248        do {
     249            $lastsync = date( 'Y-m-d H:i:s', strtotime( get_option( 'ljapi_lastsync' ) ) );
     250            $synclist = $this->lj_ixr( 'syncitems', array( 'ver' => 1, 'lastsync' => $lastsync ) );
     251            $this->log( $synclist, 'ljimport-items-' . $total . '.txt' );
     252           
     253            // Keep track of if we've downloaded everything
     254            $total = $synclist['total'];
     255            $count = $synclist['count'];
     256       
     257            foreach ( $synclist['syncitems'] as $event ) {
     258                if ( substr( $event['item'], 0, 2 ) == 'L-' ) {
     259                    $sync_item_times[ str_replace( 'L-', '', $event['item'] ) ] = $event['time'];
     260                    if ( $event['time'] > $lastsync )
     261                        $lastsync = $event['time'];
     262                }
     263            }
     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 );
     269        } while ( $total > $count );
     270        // endwhile - all post meta is cached locally
     271        $this->log( $sync_item_times, 'ljimport-post-mod-times.txt' );
     272       
    55273        echo '<ol>';
    56         foreach ($posts as $post) {
    57             preg_match('|<subject>(.*?)</subject>|is', $post, $post_title);
    58             $post_title = $wpdb->escape(trim($post_title[1]));
    59             if ( empty($post_title) ) {
    60                 preg_match('|<itemid>(.*?)</itemid>|is', $post, $post_title);
    61                 $post_title = $wpdb->escape(trim($post_title[1]));
    62             $post_content = preg_replace('|<lj\s+user\s*=\s*["\']([\w-]+)["\']>|', '<a href="http://$1.livejournal.com/">$1</a>', $post_content);
    63             }
    64 
    65             preg_match('|<eventtime>(.*?)</eventtime>|is', $post, $post_date);
    66             $post_date = strtotime($post_date[1]);
    67             $post_date = date('Y-m-d H:i:s', $post_date);
    68 
    69             preg_match('|<event>(.*?)</event>|is', $post, $post_content);
    70             $post_content = str_replace(array ('<![CDATA[', ']]>'), '', trim($post_content[1]));
    71             $post_content = $this->unhtmlentities($post_content);
    72 
    73             // Clean up content
    74             $post_content = preg_replace_callback('|<(/?[A-Z]+)|', create_function('$match', 'return "<" . strtolower($match[1]);'), $post_content);
    75             $post_content = str_replace('<br>', '<br />', $post_content);
    76             $post_content = str_replace('<hr>', '<hr />', $post_content);
    77             $post_content = $wpdb->escape($post_content);
    78 
    79             $post_author = $current_user->ID;
    80             $post_status = 'publish';
    81 
     274       
     275        $imported_count = (int) get_option( 'ljapi_imported_count' );
     276        $lastsync = get_option( 'ljapi_lastsync_posts' );
     277        if ( !$lastsync )
     278            update_option( 'ljapi_lastsync_posts', date( 'Y-m-d H:i:s', 0 ) );
     279       
     280        do {
     281            $lastsync = date( 'Y-m-d H:i:s', strtotime( get_option( 'ljapi_lastsync_posts' ) ) );
     282           
     283            // Get the batch of items that match up with the syncitems list
     284            $itemlist = $this->lj_ixr( 'getevents', array( 'ver' => 1,
     285                                                            'selecttype' => 'syncitems',
     286                                                            'lineendings' => 'pc',
     287                                                            'lastsync' => $lastsync ) );
     288            $this->log( $itemlist, 'ljimport-posts-' . $imported_count . '.txt' );
     289            if ( is_wp_error( $itemlist ) )
     290                return $itemlist;
     291            if ( $num = count( $itemlist['events'] ) ) {
     292                foreach ( $itemlist['events'] as $event ) {
     293                    $imported_count++;
     294                    $this->import_post( $event );
     295                    if ( $sync_item_times[ $event['itemid'] ] > $lastsync )
     296                        $lastsync = $sync_item_times[ $event['itemid'] ];
     297                }
     298                update_option( 'ljapi_lastsync_posts',  $lastsync );
     299                update_option( 'ljapi_imported_count',  $imported_count );
     300                update_option( 'ljapi_last_sync_count', $num );
     301            }
     302        } while ( $num > 0 );
     303
     304        echo '</ol>';
     305    }
     306   
     307    function import_post( $post ) {
     308        global $wpdb;
     309       
     310        // Make sure we haven't already imported this one
     311        if ( $this->get_wp_post_ID( $post['itemid'] ) )
     312            return;
     313       
     314        $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
     318
     319        // For some reason, LJ sometimes sends a date as "2004-04-1408:38:00" (no space btwn date/time)
     320        $post_date = $post['eventtime'];
     321        if ( 18 == strlen( $post_date ) )
     322            $post_date = substr( $post_date, 0, 10 ) . ' ' . substr( $post_date, 10 );
     323       
     324        // Cleaning up and linking the title
     325        $post_title = trim( $post['subject'] );
     326        $post_title = $this->translate_lj_user( $post_title ); // Translate it, but then we'll strip the link
     327        $post_title = strip_tags( $post_title ); // Can't have tags in the title in WP
     328        $post_title = $wpdb->escape( $post_title );
     329       
     330        // Clean up content
     331        $post_content = $post['event'];
     332        $post_content = preg_replace_callback( '|<(/?[A-Z]+)|', create_function( '$match', 'return "<" . strtolower( $match[1] );' ), $post_content );
     333        // XHTMLize some tags
     334        $post_content = str_replace( '<br>', '<br />', $post_content );
     335        $post_content = str_replace( '<hr>', '<hr />', $post_content );
     336        // lj-cut ==>  <!--more-->
     337        $post_content = preg_replace( '|<lj-cut text="([^"]*)">|is', '<!--more $1-->', $post_content );
     338        $post_content = str_replace( array( '<lj-cut>', '</lj-cut>' ), array( '<!--more-->', '' ), $post_content );
     339        $first = strpos( $post_content, '<!--more' );
     340        $post_content = substr( $post_content, 0, $first + 1 ) . preg_replace( '|<!--more(.*)?-->|sUi', '', substr( $post_content, $first + 1 ) );
     341        // lj-user ==>  a href
     342        $post_content = $this->translate_lj_user( $post_content );
     343        $post_content = force_balance_tags( $post_content );
     344        $post_content = $wpdb->escape( $post_content );
     345       
     346        // Handle any tags associated with the post
     347        $tags_input = !empty( $post['props']['taglist'] ) ? $post['props']['taglist'] : '';
     348       
     349        // Check if comments are closed on this post
     350        $comment_status = !empty( $post['props']['opt_nocomments'] ) ? 'closed' : 'open';
     351
     352        echo '<li>';
     353        if ( $post_id = post_exists( $post_title, $post_content, $post_date ) ) {
     354            printf( __( 'Post <strong>%s</strong> already exists.' ), stripslashes( $post_title ) );
     355        } else {
     356            printf( __( 'Importing post <strong>%s</strong>...' ), stripslashes( $post_title ) );
     357            $postdata = compact( 'post_author', 'post_date', 'post_content', 'post_title', 'post_status', 'post_password', 'tags_input', 'comment_status' );
     358            $post_id = wp_insert_post( $postdata );
     359            if ( is_wp_error( $post_id ) )
     360                return $post_id;
     361            if ( !$post_id ) {
     362                _e( "Couldn't get post ID" );
     363                echo '</li>';
     364                break;
     365            }
     366            $postdata['post_ID']   = $post_id;
     367            $postdata['lj_itemid'] = $post['itemid'];
     368            $this->log( $postdata, 'ljimport-post-' . $post_id . '.txt' );
     369           
     370            // Handle all the metadata for this post
     371            $this->insert_postmeta( $post_id, $post );
     372        }
     373        echo '</li>';
     374    }
     375   
     376    // Convert lj-user tags to links to that user
     377    function translate_lj_user( $str ) {
     378        return preg_replace( '|<lj\s+user\s*=\s*["\']([\w-]+)["\']>|', '<a href="http://$1.livejournal.com/" class="lj-user">$1</a>', $str );
     379    }
     380   
     381    function insert_postmeta( $post_id, $post ) {
     382        // Need the original LJ id for comments
     383        add_post_meta( $post_id, 'lj_itemid', $post['itemid'] );
     384       
     385        // And save the permalink on LJ in case we want to link back or something
     386        add_post_meta( $post_id, 'lj_permalink', $post['url'] );
     387       
     388        // Supports the following "props" from LJ, saved as lj_<prop_name> in wp_postmeta
     389        //      Adult Content - adult_content
     390        //      Location - current_coords + current_location
     391        //      Mood - current_mood (translated from current_moodid)
     392        //      Music - current_music
     393        //      Userpic - picture_keyword
     394        foreach ( array( 'adult_content', 'current_coords', 'current_location', 'current_moodid', 'current_music', 'picture_keyword' ) as $prop ) {
     395            if ( !empty( $post['props'][$prop] ) ) {
     396                if ( 'current_moodid' == $prop ) {
     397                    $prop = 'current_mood';
     398                    $val = $this->moods[ $post['props']['current_moodid'] ];
     399                } else {
     400                    $val = $post['props'][$prop];
     401                }
     402                add_post_meta( $post_id, 'lj_' . $prop, $val );
     403            }
     404        }
     405    }
     406   
     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() {
     410        // Get a session via XMLRPC
     411        $cookie = $this->lj_ixr( 'sessiongenerate', array( 'ver' => 1, 'expiration' => 'short' ) );
     412       
     413        // Comment Meta
     414       
     415        // Load previous state (if any)
     416        $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;
     423       
     424        // We need to loop over the metadata request until we have it all
     425        while ( $maxid > $highest_id ) {
     426            // 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;
     432           
     433            // Get the maxid so we know if we have them all yet
     434            preg_match( '|<maxid>(\d+)</maxid>|', $results, $matches );
     435            $maxid = !empty( $matches[1] ) ? $matches[1] : $maxid;
     436           
     437            // Parse comments and get highest id available
     438            preg_match_all( '|<comment id=\'(\d+)\'|is', $results, $matches );
     439            foreach ( $matches[1] as $id ) {
     440                if ( $id > $highest_id )
     441                    $highest_id = $id;
     442            }
     443
     444            // Parse out the list of user mappings, and add it to the known list
     445            preg_match_all( '|<usermap id=\'(\d+)\' user=\'([^\']+)\' />|', $results, $matches );
     446            foreach ( $matches[1] as $count => $userid )
     447                $this->usermap[$userid] = $matches[2][$count]; // need this in memory for translating ids => names
     448               
     449            update_option( 'ljapi_usermap',    $this->usermap );
     450            update_option( 'ljapi_maxid',      $maxid );
     451            update_option( 'ljapi_highest_id', $highest_id );
     452        }
     453        // endwhile - should have seen all comment meta at this point
     454       
     455       
     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       
     502        return true;
     503    }
     504   
     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();
     515       
     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];
     521           
     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 );
     524           
     525            // Parse this comment into an array
     526            $comment = $this->parse_comment( $comment );
     527            if ( empty( $comment['comment_post_ID'] ) )
     528                continue;
     529           
     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;
     539            }
     540        }
     541       
     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();
     548       
     549        // Don't need this XML file anymore
     550        unlink( $xml_file );
     551       
     552        return true;
     553    }
     554   
     555    function parse_comment( $comment ) {
     556        global $wpdb;
     557       
     558        // Get the top-level attributes
     559        preg_match( '|<comment([^>]+)>|i', $comment, $attribs );
     560        preg_match( '| id=\'(\d+)\'|i', $attribs[1], $matches );
     561        $lj_comment_ID = $matches[1];
     562        preg_match( '| jitemid=\'(\d+)\'|i', $attribs[1], $matches );
     563        $lj_comment_post_ID = $matches[1];
     564        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';
     570       
     571        // Clean up "subject" - this will become the first line of the comment in WP
     572        preg_match( '|<subject>(.*)</subject>|is', $comment, $matches );
     573        $comment_subject = $wpdb->escape( trim( $matches[1] ) );
     574        if ( 'Re:' == $comment_subject )
     575            $comment_subject = '';
     576       
     577        // Get the body and HTMLize it
     578        preg_match( '|<body>(.*)</body>|is', $comment, $matches );
     579        $comment_content = !empty( $comment_subject ) ? $comment_subject . "\n\n" . $matches[1] : $matches[1];
     580        $comment_content = html_entity_decode( $comment_content );
     581        $comment_content = str_replace( '&apos;', "'", $comment_content );
     582        $comment_content = wpautop( $comment_content );
     583        $comment_content = str_replace( '<br>', '<br />', $comment_content );
     584        $comment_content = str_replace( '<hr>', '<hr />', $comment_content );
     585        $comment_content = preg_replace_callback( '|<(/?[A-Z]+)|', create_function( '$match', 'return "<" . strtolower( $match[1] );' ), $comment_content );
     586        $comment_content = $wpdb->escape( trim( $comment_content ) );
     587       
     588        // Get and convert the date
     589        preg_match( '|<date>(.*)</date>|i', $comment, $matches );
     590        $comment_date = trim( str_replace( array( 'T', 'Z' ), ' ', $matches[1] ) );
     591       
     592        // Grab IP if available
     593        preg_match( '|<property name=\'poster_ip\'>(.*)</property>|i', $comment, $matches );
     594        $comment_author_IP = $matches[1];
     595       
     596        // 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];
     598        if ( get_option( 'ljapi_username' ) == $author ) {
     599            $user    = wp_get_current_user();
     600            $user_id = $user->ID;
     601            $author  = $user->display_name;
     602            $url     = trailingslashit( get_option( 'home' ) );
     603        } else {
     604            $user_id = 0;
     605            $url     = ( __( 'Anonymous' ) == $author ) ? '' : 'http://' . $author . '.livejournal.com/';           
     606        }
     607       
     608        // Send back the array of details
     609        return array( 'lj_comment_ID' => $lj_comment_ID,
     610                        'lj_comment_post_ID' => $lj_comment_post_ID,
     611                        'lj_comment_parent' => ( !empty( $lj_comment_parent ) ? $lj_comment_parent : 0 ),
     612                        'lj_comment_state' => $lj_comment_state,
     613                        'comment_post_ID' => $this->get_wp_post_ID( $lj_comment_post_ID ),
     614                        'comment_author' => $author,
     615                        'comment_author_url' => $url,
     616                        'comment_content' => $comment_content,
     617                        'comment_date' => $comment_date,
     618                        'comment_author_IP' => ( !empty( $comment_author_IP ) ? $comment_author_IP : '' ),
     619                        'comment_approved' => ( in_array( $lj_comment_state, array( 'A', 'F' ) ) ? 1 : 0 ),
     620                        'comment_agent' => 'WP LJ Importer',
     621                        'user_id' => $user_id
     622                        );
     623    }
     624   
     625   
     626    // Gets the post_ID that a LJ post has been saved as within WP
     627    function get_wp_post_ID( $post ) {
     628        global $wpdb;
     629        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 ) );
     631        return $this->postmap[$post];
     632    }
     633   
     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;
     676    }
     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();
     704           
     705        $count = count( $this->comments );
     706        for ( $c = 0; $c < $count; $c++ ) {
     707            $comment =& $this->comments[$c];
    82708            echo '<li>';
    83             if ($post_id = post_exists($post_title, $post_content, $post_date)) {
    84                 printf(__('Post <em>%s</em> already exists.'), stripslashes($post_title));
    85             } else {
    86                 printf(__('Importing post <em>%s</em>...'), stripslashes($post_title));
    87                 $postdata = compact('post_author', 'post_date', 'post_content', 'post_title', 'post_status');
    88                 $post_id = wp_insert_post($postdata);
    89                 if ( is_wp_error( $post_id ) )
    90                     return $post_id;
    91                 if (!$post_id) {
    92                     _e("Couldn't get post ID");
    93                     echo '</li>';
    94                     break;
    95                 }
    96             }
    97 
    98             preg_match_all('|<comment>(.*?)</comment>|is', $post, $comments);
    99             $comments = $comments[1];
    100 
    101             if ( $comments ) {
    102                 $comment_post_ID = (int) $post_id;
    103                 $num_comments = 0;
    104                 foreach ($comments as $comment) {
    105                     preg_match('|<event>(.*?)</event>|is', $comment, $comment_content);
    106                     $comment_content = str_replace(array ('<![CDATA[', ']]>'), '', trim($comment_content[1]));
    107                     $comment_content = $this->unhtmlentities($comment_content);
    108 
    109                     // Clean up content
    110                     $comment_content = preg_replace_callback('|<(/?[A-Z]+)|', create_function('$match', 'return "<" . strtolower($match[1]);'), $comment_content);
    111                     $comment_content = str_replace('<br>', '<br />', $comment_content);
    112                     $comment_content = str_replace('<hr>', '<hr />', $comment_content);
    113                     $comment_content = $wpdb->escape($comment_content);
    114 
    115                     preg_match('|<eventtime>(.*?)</eventtime>|is', $comment, $comment_date);
    116                     $comment_date = trim($comment_date[1]);
    117                     $comment_date = date('Y-m-d H:i:s', strtotime($comment_date));
    118 
    119                     preg_match('|<name>(.*?)</name>|is', $comment, $comment_author);
    120                     $comment_author = $wpdb->escape(trim($comment_author[1]));
    121 
    122                     preg_match('|<email>(.*?)</email>|is', $comment, $comment_author_email);
    123                     $comment_author_email = $wpdb->escape(trim($comment_author_email[1]));
    124 
    125                     $comment_approved = 1;
    126                     // Check if it's already there
    127                     if (!comment_exists($comment_author, $comment_date)) {
    128                         $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_date', 'comment_content', 'comment_approved');
    129                         $commentdata = wp_filter_comment($commentdata);
    130                         wp_insert_comment($commentdata);
    131                         $num_comments++;
    132                     }
    133                 }
    134             }
    135             if ( $num_comments ) {
    136                 echo ' ';
    137                 printf(__ngettext('(%s comment)', '(%s comments)', $num_comments), $num_comments);
    138             }
     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           
    139718            echo '</li>';
    140719        }
     720       
     721        // Remove the file now that we're done with it
     722        @unlink( $filename );
     723
    141724        echo '</ol>';
    142     }
    143 
    144     function import() {
    145         $file = wp_import_handle_upload();
    146         if ( isset($file['error']) ) {
    147             echo $file['error'];
    148             return;
    149         }
    150 
    151         $this->file = $file['file'];
    152         $result = $this->import_posts();
    153         if ( is_wp_error( $result ) )
    154             return $result;
    155         wp_import_cleanup($file['id']);
    156         do_action('import_done', 'livejournal');
    157 
    158         echo '<h3>';
    159         printf(__('All done. <a href="%s">Have fun!</a>'), get_option('home'));
    160         echo '</h3>';
    161     }
    162 
     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           
     750    function lj_ixr() {
     751        if ( $challenge = $this->ixr->query( 'LJ.XMLRPC.getchallenge' ) ) {
     752            $challenge = $this->ixr->getResponse();
     753        }
     754        if ( isset( $challenge['challenge'] ) ) {
     755            $params = array( 'username' => $this->username,
     756                            'auth_method' => 'challenge',
     757                            'auth_challenge' => $challenge['challenge'],
     758                            'auth_response' => md5( $challenge['challenge'] . md5( $this->password ) ) );
     759        } else {
     760            return new WP_Error( 'IXR', __( 'LiveJournal does not appear to be responding right now. Please try again later.' ) );
     761        }
     762       
     763        $args = func_get_args();
     764        $method = array_shift( $args );
     765        if ( isset( $args[0] ) )
     766            $params = array_merge( $params, $args[0] );
     767        if ( $this->ixr->query( 'LJ.XMLRPC.' . $method, $params ) ) {
     768            return $this->ixr->getResponse();
     769        } else {
     770            $this->log( $this->ixr->message, 'ljimport-error-' . $method . '.txt' );
     771            return new WP_Error( 'IXR', __( 'XML-RPC Request Failed - ' ) . $this->ixr->getErrorCode() . ': ' . $this->ixr->getErrorMessage() );
     772        }
     773    }
     774   
    163775    function dispatch() {
    164         if (empty ($_GET['step']))
     776        if ( empty( $_REQUEST['step'] ) )
    165777            $step = 0;
    166778        else
    167             $step = (int) $_GET['step'];
     779            $step = (int) $_REQUEST['step'];
    168780
    169781        $this->header();
    170 
    171         switch ($step) {
     782       
     783        switch ( $step ) {
     784            case -1 :
     785                $this->cleanup();
     786                // Intentional no break
    172787            case 0 :
    173788                $this->greet();
    174789                break;
    175790            case 1 :
    176                 check_admin_referer('import-upload');
    177                 $result = $this->import();
     791            case 2 :
     792                $this->ixr = new IXR_Client( $this->ixr_url );
     793                // Intentional no break
     794            case 3 :
     795            case 4 :
     796            case 5 :
     797                check_admin_referer( 'lj-api-import' );
     798                $result = $this->{ 'step' . $step }();
    178799                if ( is_wp_error( $result ) )
    179800                    echo $result->get_error_message();
     
    184805    }
    185806
    186     function LJ_Import() {
    187         // Nothing.
     807    // Check form inputs and start importing posts
     808    function step1() {
     809        // Get details from form or from DB
     810        if ( !empty( $_POST['lj_username'] ) && !empty( $_POST['lj_password'] ) ) {
     811            // Store details for later
     812            $this->username = $_POST['lj_username'];
     813            $this->password = $_POST['lj_password'];
     814            update_option( 'ljapi_username', $this->username );
     815            update_option( 'ljapi_password', $this->password );
     816        } else {
     817            $this->username = get_option( 'ljapi_username' );
     818            $this->password = get_option( 'ljapi_password' );
     819        }
     820   
     821        // This is the password to set on protected posts
     822        if ( !empty( $_POST['protected_password'] ) ) {
     823            $this->protected_password = $_POST['protected_password'];
     824            update_option( 'ljapi_protected_password', $this->protected_password );
     825        } else {
     826            $this->protected_password = get_option( 'ljapi_protected_password' );
     827        }
     828       
     829        // Login to confirm the details are correct
     830        if ( empty( $this->username ) || empty( $this->password ) ) {
     831            ?>
     832            <p><?php _e( 'Please enter your LiveJournal username <em>and</em> password so we can download your posts and comments.' ) ?></p>
     833            <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>
     834            <?php
     835            return;
     836        }
     837        $login = $this->lj_ixr( 'login' );
     838        if ( is_wp_error( $login ) ) {
     839            if ( 100 == $this->ixr->getErrorCode() || 101 == $this->ixr->getErrorCode() ) {
     840                ?>
     841                <p><?php _e( 'Logging in to LiveJournal failed. Check your username and password and try again.' ) ?></p>
     842                <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>
     843                <?php
     844                return;
     845            } else {
     846                return $login;
     847            }
     848        }
     849       
     850        // 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' );
     856       
     857        echo '<h3>' . __( 'Importing Posts' ) . '</h3>';
     858        echo '<p>' . __( "We're downloading and importing all your LiveJournal posts..." ) . '</p>';
     859        ob_flush(); flush();
     860       
     861        // Now do the grunt work
     862        set_time_limit( 0 );
     863        $result = $this->import_posts();
     864        if ( is_wp_error( $result ) ) {
     865            if ( 406 == $this->ixr->getErrorCode() ) {
     866                ?>
     867                <p><strong><?php _e( 'Uh oh &ndash; LiveJournal has disconnected us because we made too many requests to their servers too quickly.' ) ?></strong></p>
     868                <p><strong><?php _e( "We'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>
     869                <?php
     870                return;
     871            } else {
     872                return $result;
     873            }
     874        }
     875       
     876        echo '<p>' . __( "Your posts have all been imported, but wait - there's more! Now we need to process &amp; import your comments." ) . '</p>';
     877        echo $this->next_step( 2, __( 'Download my comments &raquo;' ) );
     878        $this->auto_submit();
     879    }
     880   
     881    // Download comments to local XML
     882    function step2() {
     883        set_time_limit( 0 );
     884        update_option( 'ljapi_step', 2 );
     885        $this->username = get_option( 'ljapi_username' );
     886        $this->password = get_option( 'ljapi_password' );
     887       
     888        echo '<h3>' . __( 'Downloading Comments' ) . '</h3>';
     889        echo '<p>' . __( 'Now we will download your comments so we can process and import them...' ) . '</p>';
     890        ob_flush(); flush();
     891       
     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 );
     919            if ( is_wp_error( $result ) )
     920                return $result;
     921
     922            update_option( 'ljapi_comment_xml_files', $files );
     923        }
     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
     935        } 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;' ) );
     938            $this->auto_submit();
     939        }
     940        echo '</div>';
     941    }
     942
     943    // Thread comments within their cache files
     944    function step4() {
     945        set_time_limit( 0 );
     946        update_option( 'ljapi_step', 4 );
     947       
     948        echo '<div id="ljapi-status">';
     949        echo '<h3>' . __( 'Threading Comments' ) . '</h3>';
     950        echo '<p>' . __( 'Re-building your conversation threads ready for import...' ) . '</p>';
     951        ob_flush(); flush();
     952       
     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 );
     962        }
     963       
     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();
     979        }
     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 );
     987       
     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 );
     1005        }
     1006       
     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        }
     1024        echo '</div>';
     1025    }
     1026   
     1027    // Returns the HTML for a link to the next page
     1028    function next_step( $next_step, $label, $id = 'ljapi-next-form' ) {
     1029        $str  = '<form action="admin.php?import=livejournal" method="post" id="' . $id . '">';
     1030        $str .= wp_nonce_field( 'lj-api-import', '_wpnonce', true, false );
     1031        $str .= wp_referer_field( false );
     1032        $str .= '<input type="hidden" name="step" id="step" value="' . $next_step . '" />';
     1033        $str .= '<p><input type="submit" class="button-primary" value="' . attribute_escape( $label ) . '" /> <span id="auto-message"></span></p>';
     1034        $str .= '</form>';
     1035       
     1036        return $str;
     1037    }
     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 ) {
     1043        ?><script type="text/javascript">
     1044            next_counter = <?php echo $seconds ?>;
     1045            jQuery(document).ready(function(){
     1046                ljapi_msg();
     1047            });
     1048           
     1049            function ljapi_msg() {
     1050                str = '<?php _e( "Continuing in %d" ) ?>';
     1051                jQuery( '#<?php echo $msg ?>' ).text( str.replace( /%d/, next_counter ) );
     1052                if ( next_counter <= 0 ) {
     1053                    if ( jQuery( '#<?php echo $id ?>' ).length ) {
     1054                        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" />';
     1057                        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'] ?>'});
     1062                        return;
     1063                    }
     1064                }
     1065                next_counter = next_counter - 1;
     1066                setTimeout('ljapi_msg()', 1000);
     1067            }
     1068        </script><?php
     1069    }
     1070   
     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 ) {
     1074        ?><script type="text/javascript">
     1075            next_counter = <?php echo $seconds ?>;
     1076            jQuery(document).ready(function(){
     1077                ljapi_msg();
     1078            });
     1079           
     1080            function ljapi_msg() {
     1081                str = '<?php _e( "Continuing in %d" ) ?>';
     1082                jQuery( '#<?php echo $msg ?>' ).text( str.replace( /%d/, next_counter ) );
     1083                if ( next_counter <= 0 ) {
     1084                    if ( jQuery( '#<?php echo $id ?>' ).length ) {
     1085                        jQuery( "#<?php echo $id ?> input[type='submit']" ).hide();
     1086                        str = '<?php _e( "Continuing" ) ?> <img src="images/loading-publish.gif" alt="" id="processing" align="top" />';
     1087                        jQuery( '#<?php echo $msg ?>' ).html( str );
     1088                        jQuery( '#<?php echo $id ?>' ).submit();
     1089                        return;
     1090                    }
     1091                }
     1092                next_counter = next_counter - 1;
     1093                setTimeout('ljapi_msg()', 1000);
     1094            }
     1095        </script><?php
     1096    }
     1097
     1098    // Remove all options used during import process
     1099    function cleanup() {
     1100        delete_option( 'ljapi_username' );
     1101        delete_option( 'ljapi_password' );
     1102        delete_option( 'ljapi_protected_password' );
     1103        delete_option( 'ljapi_total' );
     1104        delete_option( 'ljapi_count' );
     1105        delete_option( 'ljapi_lastsync' );
     1106        delete_option( 'ljapi_last_sync_count' );
     1107        delete_option( 'ljapi_sync_item_times' );
     1108        delete_option( 'ljapi_lastsync_posts' );
     1109        delete_option( 'ljapi_imported_count' );
     1110        delete_option( 'ljapi_maxid' );
     1111        delete_option( 'ljapi_usermap' );
     1112        delete_option( 'ljapi_highest_id' );
     1113        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' );
     1120        delete_option( 'ljapi_step' );
     1121    }
     1122   
     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
     1162    function LJ_API_Import() {
     1163        $this->__construct();
     1164    }
     1165   
     1166    function __construct() {
     1167        // Nothing
    1881168    }
    1891169}
    1901170
    191 $livejournal_import = new LJ_Import();
    192 
    193 register_importer('livejournal', __('LiveJournal'), __('Import posts from a LiveJournal XML export file.'), array ($livejournal_import, 'dispatch'));
     1171$lj_api_import = new LJ_API_Import();
     1172
     1173register_importer( 'livejournal', __( 'LiveJournal' ), __( 'Import posts from LiveJournal using their API.' ), array( $lj_api_import, 'dispatch' ) );
    1941174?>
Note: See TracChangeset for help on using the changeset viewer.