mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)
Mark Smith ([staff profile] mark) wrote in [site community profile] changelog2009-02-25 10:25 am

[dw-free] Allow importing of your journal from another LiveJournal-based site.

[commit: http://hg.dwscoalition.org/dw-free/rev/821e7159136b]

http://bugs.dwscoalition.org/show_bug.cgi?id=114

More work on the importer. Enable importing friend groups, friends, and
entries. This is still very experimental and needs a way to bubble up
errors to the user.

Patch by [staff profile] mark.

--------------------------------------------------------------------------------
diff -r 913ec83595d8 -r 821e7159136b bin/upgrading/update-db-general.pl
--- a/bin/upgrading/update-db-general.pl	Tue Feb 24 09:09:34 2009 +0000
+++ b/bin/upgrading/update-db-general.pl	Wed Feb 25 10:25:53 2009 +0000
@@ -3008,6 +3008,21 @@ CREATE TABLE import_data (
 )
 EOC
 
+# we don't store this in userprops because we need to index this
+# backwards and load it easily...
+register_tablecreate("import_usermap", <<'EOC');
+CREATE TABLE import_usermap (
+    hostname VARCHAR(255),
+    username VARCHAR(255),
+    identity_userid INT UNSIGNED,
+    feed_userid INT UNSIGNED,
+
+    PRIMARY KEY (hostname, username),
+    INDEX (identity_userid),
+    INDEX (feed_userid)
+)
+EOC
+
 # NOTE: new table declarations go ABOVE here ;)
 
 
diff -r 913ec83595d8 -r 821e7159136b cgi-bin/DW/Worker/ContentImporter.pm
--- a/cgi-bin/DW/Worker/ContentImporter.pm	Tue Feb 24 09:09:34 2009 +0000
+++ b/cgi-bin/DW/Worker/ContentImporter.pm	Wed Feb 25 10:25:53 2009 +0000
@@ -83,91 +83,6 @@ sub merge_watch {
 }
 
 
-=head2 C<< $class->post_event( $user, $hashref, $event, $item_errors ) >>
-
-$event is a hashref representation of a single entry, with the following format:
-
-  {
-    subject => 'My Entry',
-    event => 'I DID STUFF!!!!!',
-    security => 'usemask',
-    allowmask => 1,
-
-    eventtime => 'yyyy-mm-dd hh:mm:ss',
-    props => {
-        heres_a_userprop => "there's a userprop",
-        and_another_little => "userprop",
-    }
-    key => 'some_uniqe_key', # generally the permalink to the old entry, otherwise something unique (across *all* import sources possible)
-    url => 'http://permalink.tld/', # permalink to the old entry
-  }
-
-$item_errors is an arrayref of errors to be formatted nicely with a link to old and new entries.
-
-=cut
-sub post_event {
-    my ( $class, $u, $opts, $evt, $item_errors ) = @_;
-
-    return if $opts->{entry_map}->{$evt->{key}};
-
-    my ( $yr, $month, $day, $hr, $min, $sec ) = $evt->{eventtime} =~ m/([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/;
-    my %proto = (
-        lineendings => 'unix',
-        subject => $evt->{subject},
-        event => $evt->{event},
-        security => $evt->{security},
-        allowmask => $evt->{allowmask},
-
-        year => $yr,
-        mon => $month,
-        day => $day,
-        hour => $hr,
-        min => $min,
-    );
-
-    my $props = $evt->{props};
-
-    # this is a list of props that actually exist on this site
-    # but have been shown to cause failures importing that entry.
-    my %bad_props = (
-        current_coords => 1,
-    );
-    foreach my $prop ( keys %$props ) {
-        my $p = LJ::get_prop( "log", $prop );
-
-        # skip over system and non-existant props
-        next unless $p;
-        next if ( $p->{ownership} eq 'system' );
-        next if ( $bad_props{$prop} );
-
-        $proto{"prop_$prop"} = $props->{$prop};
-    };
-
-    # Overwrite these here in case we're importing from an imported journal (hey, it could happen)
-    $proto{prop_opt_backdated} = '1';
-    $proto{prop_import_source} = $evt->{key};
-
-    my %res;
-    LJ::do_request({ 'mode' => 'postevent',
-                     'user' => $u->{'user'},
-                     'ver'  => $LJ::PROTOCOL_VER,
-                     %proto },
-                   \%res, { 'u' => $u,
-                            'noauth' => 1, });
-
-    my $errors = $opts->{errors};
-    if ( $res{success} eq 'FAIL' ) {
-        push @$errors, "Entry from $evt->{url}: $res{errmsg}";
-    } else {
-        my $itemid = $res{itemid};
-        $u->do( "UPDATE log2 SET logtime = ? where journalid = ? and jitemid = ?", undef, $evt->{realtime}, $u->userid, $itemid );
-        $opts->{entry_map}->{$evt->{key}} = $itemid;
-        foreach my $err ( @$item_errors ) {
-            push @$errors, "Entry at $res{url}: $err ( from $evt->{url} )";
-        }
-    }
-}
-
 =head2 C<< $class->post_event( $user, $hashref, $comment ) >>
 
 $event is a hashref representation of a single comment, with the following format:
@@ -228,31 +143,6 @@ sub insert_comment {
     my $jtalkid = LJ::Talk::Post::enter_imported_comment( $u, $parent, $item, $comment, $date, \$errref );
     return undef unless $jtalkid;
     return $jtalkid;
-}
-
-=head2 C<< $class->get_entry_map( $user, $hashref )
-
-Returns a hashref mapping import_source keys to jitemids
-
-=cut
-sub get_entry_map {
-    my ( $class, $u, $opts ) = @_;
-    return $opts->{entry_map} if $opts->{entry_map};
-
-    my $p = LJ::get_prop( "log", "import_source" );
-    return {} unless $p;
-
-    my $dbr = LJ::get_cluster_reader( $u );
-    my %map;
-    my $sth = $dbr->prepare( "SELECT jitemid, value FROM logprop2 WHERE journalid = ? AND propid = ?" );
-
-    $sth->execute( $u->id, $p->{id} );
-
-    while ( my ( $jitemid, $value ) = $sth->fetchrow_array ) {
-        $map{$value} = $jitemid;
-    }
-
-    return \%map;
 }
 
 =head2 C<< $class->get_comment_map( $user, $hashref )
@@ -389,4 +279,4 @@ sub ok {
 }
 
 
-1;
\ No newline at end of file
+1;
diff -r 913ec83595d8 -r 821e7159136b cgi-bin/DW/Worker/ContentImporter/LiveJournal.pm
--- a/cgi-bin/DW/Worker/ContentImporter/LiveJournal.pm	Tue Feb 24 09:09:34 2009 +0000
+++ b/cgi-bin/DW/Worker/ContentImporter/LiveJournal.pm	Wed Feb 25 10:25:53 2009 +0000
@@ -316,25 +316,56 @@ sub get_feed_account_from_url {
     return undef;
 }
 
+sub get_remapped_userids {
+    my ( $class, $data, $user ) = @_;
+
+    return @{ $MAPS{$data->{hostname}}->{$user} }
+        if exists $MAPS{$data->{hostname}}->{$user};
+
+    my $dbh = LJ::get_db_writer()
+        or return;
+    my ( $oid, $fid ) = $dbh->selectrow_array(
+        'SELECT identity_userid, feed_userid FROM import_usermap WHERE hostname = ? AND username = ?',
+        undef, $data->{hostname}, $user
+    );
+
+    unless ( defined $oid ) {
+        warn "[$$] Remapping identity userid of $data->{hostname}:$user\n";
+        $oid = $class->remap_username_friend( $data, $user );
+        warn "     IDENTITY USERID IS STILL UNDEFINED\n"
+            unless defined $oid;
+    }
+
+    unless ( defined $fid ) {
+        warn "[$$] Remapping feed userid of $data->{hostname}:$user\n";
+        $fid = $class->remap_username_feed( $data, $user );
+        warn "     FEED USERID IS STILL UNDEFINED\n"
+            unless defined $fid;
+    }
+
+    $dbh->do( 'REPLACE INTO import_usermap (hostname, username, identity_userid, feed_userid) VALUES (?, ?, ?, ?)',
+              undef, $data->{hostname}, $user, $oid, $fid );
+    $MAPS{$data->{hostname}}->{$user} = [ $oid, $fid ];
+
+    return ( $oid, $fid );
+}
+
 sub remap_username_feed {
     my ( $class, $data, $username ) = @_;
 
     # canonicalize username and try to return
     $username =~ s/-/_/g;
-    return $MAPS{feed_map}->{$username}
-        if defined $MAPS{feed_map}->{$username};
 
     # don't allow identity accounts (they're not feeds by default)
     return undef
         if $username =~ m/^ext_/;
 
     # fall back to getting it from the ATOM data
-    my $url = "http://$data->{hostname}/~$username/data/atom";
+    my $url = "http://www.$data->{hostname}/~$username/data/atom";
     my $acct = $class->get_feed_account_from_url( $data, $url, $username )
         or return undef;
 
-    # store it and return
-    return $MAPS{feed_map}->{$username} = $acct;
+    return $acct;
 }
 
 sub remap_username_friend {
@@ -343,9 +374,6 @@ sub remap_username_friend {
     # canonicalize username, in case they gave us a URL version, convert it to
     # the one we know sites use
     $username =~ s/-/_/g;
-
-    return $MAPS{friend_map}->{$username}
-        if defined $MAPS{friend_map}->{$username};
 
     if ( $username =~ m/^ext_/ ) {
         my $ua = LJ::get_useragent(
@@ -366,35 +394,33 @@ sub remap_username_friend {
         if ( $url =~ m!http://(.+)\.$LJ::DOMAIN\/$! ) {
             # this appears to be a local user!
             # Map this to the local userid in feed_map too, as this is a local user.
-            my $luid = LJ::User->new_from_url( $url )->id;
-            $MAPS{feed_map}->{$username} = $luid;
-            return $luid;
+            return LJ::User->new_from_url( $url )->id;
         }
 
         my $iu = LJ::User::load_identity_user( 'O', $url, undef )
             or return undef;
-        $MAPS{friend_map}->{$username} = $iu->userid;
+        return $iu->id;
 
     } else {
         my $url_prefix = "http://$data->{hostname}/~" . $username;
         my ( $foaf_items ) = $class->get_foaf_from( $url_prefix )
             or return undef;
 
+        # if they don't have an identity section (but foaf was successful
+        # or we would have returned undef above), then they are a community
+        # or some other account without.  return 0 to signify this.
         my $ident = $foaf_items->{identity}->{url}
-            or return undef;
-        $MAPS{ident_map}->{$username} = $ident;
+            or return 0;
 
         my $iu = LJ::User::load_identity_user( 'O', $ident, undef )
             or return undef;
-        $MAPS{friend_map}->{$username} = $iu->userid;
+        return $iu->id;
     }
-
-    return $MAPS{friend_map}->{$username};
 }
 
 sub remap_lj_user {
-    my ( $class, $server, $event ) = @_;
-    $event =~ s/(<lj.+?(user|comm|syn)=["']?(.+?)["' ]?>)/<lj site="$server" $2="$3">/gi;
+    my ( $class, $data, $event ) = @_;
+    $event =~ s/(<lj.+?(user|comm|syn)=["']?(.+?)["' ]?>)/<lj site="$data->{hostname}" $2="$3">/gi;
     return $event;
 }
 
@@ -422,6 +448,7 @@ sub xmlrpc_call_helper {
     my $res;
     eval { $res = $xmlrpc->call($method, $req); };
     if ( $res && $res->fault ) {
+        warn "XMLRPC fault: " . join( ', ', map { "$_:" . $res->fault->{$_} } keys %{$res->fault || {}} ) . "\n";
         return { fault => 1 };
     }
 
diff -r 913ec83595d8 -r 821e7159136b cgi-bin/DW/Worker/ContentImporter/LiveJournal/Bio.pm
--- a/cgi-bin/DW/Worker/ContentImporter/LiveJournal/Bio.pm	Tue Feb 24 09:09:34 2009 +0000
+++ b/cgi-bin/DW/Worker/ContentImporter/LiveJournal/Bio.pm	Wed Feb 25 10:25:53 2009 +0000
@@ -60,7 +60,7 @@ sub try_work {
 
     DW::Worker::ContentImporter::Local::Bio->merge_interests( $u, $interests );
 
-    $items->{bio} = $class->remap_lj_user( $data->{hostname}, $items->{bio} );
+    $items->{bio} = $class->remap_lj_user( $data, $items->{bio} );
     DW::Worker::ContentImporter::Local::Bio->merge_bio_items( $u, $items );
 
     return $ok->();
diff -r 913ec83595d8 -r 821e7159136b cgi-bin/DW/Worker/ContentImporter/LiveJournal/Entries.pm
--- a/cgi-bin/DW/Worker/ContentImporter/LiveJournal/Entries.pm	Tue Feb 24 09:09:34 2009 +0000
+++ b/cgi-bin/DW/Worker/ContentImporter/LiveJournal/Entries.pm	Wed Feb 25 10:25:53 2009 +0000
@@ -45,30 +45,24 @@ sub try_work {
     # setup
     my $u = LJ::load_userid( $data->{userid} )
         or return $fail->( 'Unable to load target with id %d.', $data->{userid} );
+    my $entry_map = DW::Worker::ContentImporter::Local::Entries->get_entry_map( $u );
 
-    # temporary failure, this code hasn't been ported yet
-    return $fail->( 'oops, not ready yet' );
-}
-
-1;
-__END__
-
-### WORK GOES HERE
-    $opts->{entry_map} = DW::Worker::ContentImporter->get_entry_map($u,$opts);
-    my $synccount = 0;
-    my $lastsync = 0;
-    my %sync;
-    while (1) {
-        DW::Worker::ContentImporter->ratelimit_request( $opts );
-        my $hash = call_xmlrpc( $opts, 'syncitems', {lastsync => $lastsync} );
+    # load the syncitems list; but never try to load the same lastsync time twice, just
+    # in case 
+    my ( $lastsync, %tried_syncs, %sync );
+    while ( $tried_syncs{$lastsync} < 2 ) {
+        warn "[$$] Attempting lastsync = " . ( $lastsync || 'undef' ) . "\n";
+        my $hash = $class->call_xmlrpc( $data, 'syncitems', { lastsync => $lastsync } );
 
         foreach my $item ( @{$hash->{syncitems} || []} ) {
-            next unless $item->{item} =~ /L-(\d+)/;
-            $synccount++;
+            next unless $item->{item} =~ /^L-(\d+)$/;
             $sync{$1} = [ $item->{action}, $item->{time} ];
-            $lastsync = $item->{time} if $item->{'time'} gt $lastsync;
+            $lastsync = $item->{time}
+                if !defined $lastsync || $item->{time} gt $lastsync;
+            $tried_syncs{$lastsync}++;
         }
 
+        warn "     count $hash->{count} == total $hash->{total}\n";
         last if $hash->{count} == $hash->{total};
     }
 
@@ -77,30 +71,44 @@ __END__
         return $sync{$id}->[1] if @{$sync{$id} || []};
     };
 
-    my $lastgrab = 0;
-    while (1) {
+    # now get the actual events
+    while ( scalar( keys %sync ) > 0 ) {
         my $count = 0;
-        DW::Worker::ContentImporter->ratelimit_request( $opts );
-        my $hash = call_xmlrpc( $opts, 'getevents', { selecttype => 'syncitems', lastsync => $lastgrab, ver => 1, lineendings => 'unix', });
+
+        # calculate what time to get entries for
+        my @keys = sort { $sync{$a}->[1] cmp $sync{$b}->[1] } keys %sync;
+        my $lastgrab = LJ::mysql_time( LJ::mysqldate_to_time( $sync{$keys[0]}->[1] ) - 1 );
+
+        warn "[$$] Fetching from lastsync = $lastgrab forward\n";
+        my $hash = $class->call_xmlrpc( $data, 'getevents',
+            {
+                ver         => 1,
+                lastsync    => $lastgrab,
+                selecttype  => 'syncitems',
+                lineendings => 'unix',
+            }
+        );
 
         foreach my $evt ( @{$hash->{events} || []} ) {
             $count++;
+
             $evt->{realtime} = $realtime->( $evt->{itemid} );
-            $lastgrab = $evt->{realtime} if $evt->{realtime} gt $lastgrab;
             $evt->{key} = $evt->{url};
 
             # skip this if we've already dealt with it before
-            next if $opts->{entry_map}->{$evt->{key}};
+            warn "     [$evt->{itemid}] $evt->{url} // $evt->{realtime} // map=$entry_map->{$evt->{key}}\n";
+            my $sync = delete $sync{$evt->{itemid}};
+            next if $entry_map->{$evt->{key}} || !defined $sync;
+
             # clean up event for LJ
-
             my @item_errors;
 
             # remap friend groups
             my $allowmask = $evt->{allowmask};
-            my $newmask = remap_groupmask( $opts, $allowmask );
+            my $newmask = $class->remap_groupmask( $data, $allowmask );
 
-            # Bah. Assume private. This shouldn't relaly happen, but
-            # a good sanity check.
+            # if we are unable to determine a good groupmask, then fall back to making
+            # the entry private and mark the error.
             if ( $allowmask != 1 && $newmask == 1 ) { 
                 $newmask = 0;
                 push @item_errors, "Could not determine groups.";
@@ -128,19 +136,26 @@ __END__
                 push @item_errors, "Entry contained a template tag, please manually re-add the templated content.";
             }
 
-            $evt->{event} = remap_lj_user( $opts, $event );
+            $evt->{event} = $class->remap_lj_user( $data, $event );
 
             # actually post it
-            DW::Worker::ContentImporter->post_event( $u, $opts, $evt, \@item_errors );
+            my $res = DW::Worker::ContentImporter::Local::Entries->post_event( $data, $entry_map, $u, $evt, \@item_errors );
+
+# FIXME: do something with the return code and @item_errors ... other than
+# printing them to STDERR of course ...
+            if ( $res ) {
+                warn "     imported!\n";
+            }  else {
+                warn "     failed!\n";
+            }
+            warn "       $_\n" foreach @item_errors;
         }
 
-        last unless $count && $lastgrab;
+        warn "     count = $count && lastgrab = $lastgrab\n";
     }
-
-    $opts->{no_entries} = 1;
 
     return $ok->();
 }
 
 
-1;
\ No newline at end of file
+1;
diff -r 913ec83595d8 -r 821e7159136b cgi-bin/DW/Worker/ContentImporter/LiveJournal/Friends.pm
--- a/cgi-bin/DW/Worker/ContentImporter/LiveJournal/Friends.pm	Tue Feb 24 09:09:34 2009 +0000
+++ b/cgi-bin/DW/Worker/ContentImporter/LiveJournal/Friends.pm	Wed Feb 25 10:25:53 2009 +0000
@@ -51,23 +51,31 @@ sub try_work {
 
     my ( @friends, @feeds );
     foreach my $friend (@{ $r->{friends} || [] }) {
-        my $local_userid = $class->remap_username_friend( $data, $friend->{username} );
+        my ( $local_oid, $local_fid ) = $class->get_remapped_userids( $data, $friend->{username} );
+
         push @friends, {
-            userid => $local_userid,
+            userid => $local_oid,
             groupmask => $class->remap_groupmask( $data, $friend->{groupmask} ),
-        } if $local_userid;
+        } if $local_oid;
 
-        $local_userid = $class->remap_username_feed( $data, $friend->{username} );
         push @feeds, {
             fgcolor => $friend->{fgcolor},
             bgcolor => $friend->{bgcolor},
-            userid => $local_userid,
-        } if $local_userid;
+            userid => $local_fid,
+        } if $local_fid;
     }
 
     DW::Worker::ContentImporter->merge_trust( $u, $opts, \@friends );
     DW::Worker::ContentImporter->merge_watch( $u, $opts, \@feeds );
 
+    # schedule events import
+    my $dbh = LJ::get_db_writer();
+    $dbh->do(
+        q{UPDATE import_items SET status = 'ready'
+          WHERE userid = ? AND item = 'lj_entries' AND import_data_id = ? AND status = 'init'},
+        undef, $u->id, $opts->{import_data_id}        
+    );
+
     return $ok->();
 }
 
diff -r 913ec83595d8 -r 821e7159136b cgi-bin/DW/Worker/ContentImporter/Local/Entries.pm
--- a/cgi-bin/DW/Worker/ContentImporter/Local/Entries.pm	Tue Feb 24 09:09:34 2009 +0000
+++ b/cgi-bin/DW/Worker/ContentImporter/Local/Entries.pm	Wed Feb 25 10:25:53 2009 +0000
@@ -26,7 +26,135 @@ DW::Worker::ContentImporter::Local::Entr
 
 These functions are part of the Saving API for entries.
 
+=head2 C<< $class->get_entry_map( $user, $hashref )
+
+Returns a hashref mapping import_source keys to jitemids
+
 =cut
 
+sub get_entry_map {
+    my ( $class, $u ) = @_;
 
-1;
\ No newline at end of file
+    my $p = LJ::get_prop( "log", "import_source" );
+    return {} unless $p;
+
+    my $dbr = LJ::get_cluster_reader( $u );
+    my %map;
+    my $sth = $dbr->prepare( "SELECT jitemid, value FROM logprop2 WHERE journalid = ? AND propid = ?" );
+
+    $sth->execute( $u->id, $p->{id} );
+
+    while ( my ( $jitemid, $value ) = $sth->fetchrow_array ) {
+        $map{$value} = $jitemid;
+    }
+
+    return \%map;
+}
+
+=head2 C<< $class->post_event( $hashref, $u, $event, $item_errors ) >>
+
+$event is a hashref representation of a single entry, with the following format:
+
+  {
+    # standard event values
+    subject => 'My Entry',
+    event => 'I DID STUFF!!!!!',
+    security => 'usemask',
+    allowmask => 1,
+    eventtime => 'yyyy-mm-dd hh:mm:ss',
+    props => {
+        heres_a_userprop => "there's a userprop",
+        and_another_little => "userprop",
+    }
+
+    # the key is a uniquely opaque string that identifies this entry.  this must be
+    # unique across all possible import sources.  the permalink may work best.
+    key => 'some_unique_key',
+
+    # a url to this entry's original location
+    url => 'http://permalink.tld/',
+  }
+
+$item_errors is an arrayref of errors to be formatted nicely with a link to old and new entries.
+
+Returns 1 on success, undef on error.
+
+=cut
+
+sub post_event {
+    my ( $class, $data, $map, $u, $evt, $errors ) = @_;
+
+    return if $map->{$evt->{key}};
+
+    my ( $yr, $month, $day, $hr, $min, $sec ) = ( $1, $2, $3, $4, $5, $6 )
+        if $evt->{eventtime} =~ m/(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/;
+
+    my %proto = (
+        lineendings => 'unix',
+        subject => $evt->{subject},
+        event => $evt->{event},
+        security => $evt->{security},
+        allowmask => $evt->{allowmask},
+
+        year => $yr,
+        mon => $month,
+        day => $day,
+        hour => $hr,
+        min => $min,
+    );
+
+    my $props = $evt->{props};
+
+    # this is a list of props that actually exist on this site
+    # but have been shown to cause failures importing that entry.
+    my %bad_props = (
+        current_coords => 1,
+        personifi_word_count => 1,
+        personifi_lang => 1,
+    );
+    foreach my $prop ( keys %$props ) {
+        next if $bad_props{$prop};
+
+        my $p = LJ::get_prop( "log", $prop )
+            or next;
+        next if $p->{ownership} eq 'system';
+
+        $proto{"prop_$prop"} = $props->{$prop};
+    };
+
+    # Overwrite these here in case we're importing from an imported journal (hey, it could happen)
+    $proto{prop_opt_backdated} = '1';
+    $proto{prop_import_source} = $evt->{key};
+
+    my %res;
+    LJ::do_request(
+        {
+            mode => 'postevent',
+            user => $u->user,
+            ver  => $LJ::PROTOCOL_VER,
+            %proto,
+        },
+        \%res,
+        {
+            u => $u,
+            noauth => 1,
+        }
+    );
+
+    if ( $res{success} eq 'FAIL' ) {
+        push @$errors, "Entry from $evt->{url}: $res{errmsg}";
+        return undef;
+
+    } else {
+        $u->do( "UPDATE log2 SET logtime = ? where journalid = ? and jitemid = ?",
+                undef, $evt->{realtime}, $u->userid, $res{itemid} );
+        $map->{$evt->{key}} = $res{itemid};
+        return 1;
+
+    }
+
+    # flow will never get here
+}
+
+
+1;
diff -r 913ec83595d8 -r 821e7159136b htdocs/misc/import.bml
--- a/htdocs/misc/import.bml	Tue Feb 24 09:09:34 2009 +0000
+++ b/htdocs/misc/import.bml	Wed Feb 25 10:25:53 2009 +0000
@@ -63,11 +63,15 @@ body<=
             ['lj_userpics', 'ready'],
             ['lj_bio', 'ready'],
             ['lj_tags', 'ready'],
+            ['lj_friendgroups', 'ready'],
+            ['lj_friends', 'init'],
+            ['lj_entries', 'init'],
         );
         # schedule userpic, bio, and tag imports
         foreach my $item (@jobs) {
             $dbh->do(
-                "INSERT INTO import_items (userid, item, status, created, import_data_id, priority) VALUES (?, ?, ?, UNIX_TIMESTAMP(), ?, UNIX_TIMESTAMP())",
+                "INSERT INTO import_items (userid, item, status, created, import_data_id, priority) " .
+                "VALUES (?, ?, ?, UNIX_TIMESTAMP(), ?, UNIX_TIMESTAMP())",
                 undef, $u->id, $item->[0], $item->[1], $id
             );
             return "Database error." if $dbh->err;
@@ -80,7 +84,8 @@ body<=
     my $dbh = LJ::get_db_writer()
         or return "No database.";
     my $imps = $dbh->selectall_arrayref(
-        'SELECT import_data_id, hostname, username, password_md5 FROM import_data WHERE userid = ? ORDER BY import_data_id DESC LIMIT 10',
+        'SELECT import_data_id, hostname, username, password_md5 FROM import_data WHERE userid = ? ' .
+        'ORDER BY import_data_id DESC LIMIT 3',
         undef, $u->id
     );
 
@@ -109,7 +114,7 @@ body<=
         foreach my $impid ( sort { $b <=> $a } keys %s ) {
             my $h = $s{$impid};
             $ret .= "<tr><td colspan='4'>&nbsp;</td></tr>";
-            $ret .= "<tr><td colspan='4' style='background-color: #cccccc;'>$refresh <strong>$h->{host}</strong> | $h->{user} | $h->{pw}</td></tr>";
+            $ret .= "<tr><td colspan='4' style='background-color: #cccccc;'>$refresh <strong>$h->{host}</strong> | $h->{user}</td></tr>";
             foreach my $item ( sort keys %{$h->{items}} ) {
                 my $i = $h->{items}->{$item};
                 $ret .= "<tr><td><em>$item</em></td>";
@@ -132,7 +137,8 @@ body<=
     $ret .= "<select name='hostname'><option value='livejournal.com'>LiveJournal.com</option></select><br />";
     $ret .= "<br />";
     $ret .= "Clicking submit below will cause import jobs to be queued to import your userpics, your ";
-    $ret .= "tags, and your bio fields.  You can check this page for status.";
+    $ret .= "tags, and your bio fields.  You can check this page for status.<br />";
+    $ret .= "<strong>This option now imports your friend groups, friends, and entries.</strong><br />";
     $ret .= "<input type='submit' value='Do it!'></form>";
     return $ret;
 }
--------------------------------------------------------------------------------