fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2010-08-31 04:55 am

[dw-free] update communitylib.pl to use user methods

[commit: http://hg.dwscoalition.org/dw-free/rev/4dc0abe1fabe]

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

Modernization: use object methods rather than class methods.
($u->do_something, rather than LJ::do_something( $u ) )

Patch by [personal profile] kareila.

Files modified:
  • cgi-bin/DW/Logic/ProfilePage.pm
  • cgi-bin/DW/Logic/UserLinkBar.pm
  • cgi-bin/DW/Widget/CommunityManagement.pm
  • cgi-bin/LJ/Community.pm
  • cgi-bin/LJ/Console/Command/Community.pm
  • cgi-bin/LJ/User.pm
  • cgi-bin/LJ/Widget/CreateAccountInviter.pm
  • cgi-bin/communitylib.pl
  • cgi-bin/modperl_subs.pl
  • cgi-bin/weblib.pl
  • htdocs/approve.bml
  • htdocs/community/join.bml
  • htdocs/community/leave.bml
  • htdocs/community/manage.bml
  • htdocs/community/members.bml
  • htdocs/community/moderate.bml
  • htdocs/community/pending.bml
  • htdocs/community/sentinvites.bml
  • htdocs/community/settings.bml
  • htdocs/manage/invites.bml
  • htdocs/reject.bml
  • htdocs/tools/endpoints/changerelation.bml
  • t/console-community.t
  • t/esn-journalnewentry.t
  • t/wtf.t
--------------------------------------------------------------------------------
diff -r a7f858ce42d3 -r 4dc0abe1fabe cgi-bin/DW/Logic/ProfilePage.pm
--- a/cgi-bin/DW/Logic/ProfilePage.pm	Tue Aug 31 11:16:42 2010 +0800
+++ b/cgi-bin/DW/Logic/ProfilePage.pm	Tue Aug 31 12:54:57 2010 +0800
@@ -511,7 +511,7 @@ sub _basic_info_comm_membership {
 
     return $ret unless $u->is_community;
 
-    my ( $membership, $postlevel ) = LJ::get_comm_settings( $u );
+    my ( $membership, $postlevel ) = $u->get_comm_settings;
 
     my $membership_string = LJ::Lang::ml( '.commsettings.membership.open' );
     if ( $membership eq "moderated" ) {
@@ -537,7 +537,7 @@ sub _basic_info_comm_postlevel {
 
     return $ret unless $u->is_community;
 
-    my ( $membership, $postlevel ) = LJ::get_comm_settings( $u );
+    my ( $membership, $postlevel ) = $u->get_comm_settings;
 
     my $postlevel_string = LJ::Lang::ml( '.commsettings.postlevel.members' );
     if ( $postlevel eq "select" ) {
diff -r a7f858ce42d3 -r 4dc0abe1fabe cgi-bin/DW/Logic/UserLinkBar.pm
--- a/cgi-bin/DW/Logic/UserLinkBar.pm	Tue Aug 31 11:16:42 2010 +0800
+++ b/cgi-bin/DW/Logic/UserLinkBar.pm	Tue Aug 31 12:54:57 2010 +0800
@@ -149,7 +149,7 @@ sub manage_membership {
 
         # if logged out, OR, not a member
         } else {
-            my @comm_settings = LJ::get_comm_settings($u);
+            my @comm_settings = $u->get_comm_settings;
             my $closed = ( $comm_settings[0] && $comm_settings[0] eq 'closed' ) ? 1 : 0;
 
             my $link = {
diff -r a7f858ce42d3 -r 4dc0abe1fabe cgi-bin/DW/Widget/CommunityManagement.pm
--- a/cgi-bin/DW/Widget/CommunityManagement.pm	Tue Aug 31 11:16:42 2010 +0800
+++ b/cgi-bin/DW/Widget/CommunityManagement.pm	Tue Aug 31 12:54:57 2010 +0800
@@ -54,14 +54,14 @@ sub render_body {
         foreach my $cu ( sort { $a->user cmp $b->user }  values %$us ) {
             next unless $cu->is_visible;
 
-            my ( $membership, $postlevel ) = LJ::get_comm_settings( $cu );
+            my ( $membership, $postlevel ) = $cu->get_comm_settings;
 
             my $pending_entries_count;
-            $pending_entries_count = LJ::get_mod_queue_count( $cu )
+            $pending_entries_count = $cu->get_mod_queue_count
                 if $mods{$cu->userid};
 
             my $pending_members_count;
-            $pending_members_count = LJ::get_pending_members_count( $cu )
+            $pending_members_count = $cu->get_pending_members_count
                 if $membership eq "moderated" && $admin{$cu->userid};
             if ( $pending_members_count || $pending_entries_count ) {
                 $list .= "<dt>" . $cu->ljuser_display;
diff -r a7f858ce42d3 -r 4dc0abe1fabe cgi-bin/LJ/Community.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/LJ/Community.pm	Tue Aug 31 12:54:57 2010 +0800
@@ -0,0 +1,620 @@
+#!/usr/bin/perl
+# This code was forked from the LiveJournal project owned and operated
+# by Live Journal, Inc. The code has been modified and expanded by
+# Dreamwidth Studios, LLC. These files were originally licensed under
+# the terms of the license supplied by Live Journal, Inc, which can
+# currently be found at:
+#
+# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
+#
+# In accordance with the original license, this code and all its
+# modifications are provided under the GNU General Public License.
+# A copy of that license can be found in the LICENSE file included as
+# part of this distribution.
+
+package LJ::User;
+
+use strict;
+use warnings;
+use LJ::Event::CommunityInvite;
+use LJ::Event::CommunityJoinRequest;
+use LJ::Event::CommunityJoinApprove;
+use LJ::Event::CommunityJoinReject;
+
+# des: Sends an invitation to a user to join a community with the passed abilities.
+# args: user to invite, community u, u of maintainer doing the invite, attrs
+# des-attrs: a hashref of abilities this user should have (e.g. member, post, unmoderated, ...)
+# returns: 1 for success, undef if failure
+sub send_comm_invite {
+    my ( $u, $cu, $mu, $attrs ) = @_;
+    $cu = LJ::want_user( $cu );
+    $mu = LJ::want_user( $mu );
+    return undef unless LJ::isu( $u ) && $cu && $mu;
+
+    # step 1: if the user has banned the community, don't accept the invite
+    return LJ::error('comm_user_has_banned') if $u->has_banned( $cu );
+
+    # step 2: lazily clean out old community invites.
+    return LJ::error('db') unless $u->writer;
+    $u->do('DELETE FROM inviterecv WHERE userid = ? AND ' .
+           'recvtime < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))',
+           undef, $u->{userid});
+
+    return LJ::error('db') unless $cu->writer;
+    $cu->do('DELETE FROM invitesent WHERE commid = ? AND ' .
+            'recvtime < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))',
+            undef, $cu->{userid});
+
+    my $dbcr = LJ::get_cluster_def_reader($u);
+    return LJ::error('db') unless $dbcr;
+    my $argstr = $dbcr->selectrow_array('SELECT args FROM inviterecv WHERE userid = ? AND commid = ?',
+                                        undef, $u->{userid}, $cu->{userid});
+
+    # step 4: exceeded outstanding invitation limit?  only if no outstanding invite
+    unless ($argstr) {
+        my $cdbcr = LJ::get_cluster_def_reader($cu);
+        return LJ::error('db') unless $cdbcr;
+        my $count = $cdbcr->selectrow_array("SELECT COUNT(*) FROM invitesent WHERE commid = ? " .
+                                            "AND userid <> ? AND status = 'outstanding'",
+                                            undef, $cu->{userid}, $u->{userid});
+
+        # for now, limit to 500 outstanding invitations per community.  if this is not enough
+        # it can be raised or put back to the old system of using community size as an indicator
+        # of how many people to allow.
+        return LJ::error('comm_invite_limit') if $count > 500;
+    }
+
+    # step 5: setup arg string as url-encoded string
+    my $newargstr = join('=1&', map { LJ::eurl($_) } @$attrs) . '=1';
+
+    # step 6: branch here to update or insert
+    if ($argstr) {
+        # merely an update, so just do it quietly
+        $u->do("UPDATE inviterecv SET args = ? WHERE userid = ? AND commid = ?",
+               undef, $newargstr, $u->{userid}, $cu->{userid});
+
+        $cu->do("UPDATE invitesent SET args = ?, status = 'outstanding' WHERE userid = ? AND commid = ?",
+                undef, $newargstr, $cu->{userid}, $u->{userid});
+    } else {
+         # insert new data, as this is a new invite
+         $u->do("INSERT INTO inviterecv VALUES (?, ?, ?, UNIX_TIMESTAMP(), ?)",
+                undef, $u->{userid}, $cu->{userid}, $mu->{userid}, $newargstr);
+
+         $cu->do("REPLACE INTO invitesent VALUES (?, ?, ?, UNIX_TIMESTAMP(), 'outstanding', ?)",
+                 undef, $cu->{userid}, $u->{userid}, $mu->{userid}, $newargstr);
+    }
+
+    # Fire community invite event
+    LJ::Event::CommunityInvite->new($u, $mu, $cu)->fire if LJ::is_enabled('esn');
+
+    # step 7: error check database work
+    return LJ::error('db') if $u->err || $cu->err;
+
+    # success
+    return 1;
+}
+
+# des: Accepts an invitation a user has received.  This does all the work to make the
+#      user join the community as well as sets up privileges.
+# args: user accepting invite, community the user is joining
+# returns: 1 for success, undef if failure
+sub accept_comm_invite {
+    my ( $u, $cu ) = @_;
+    $cu = LJ::want_user( $cu );
+    return undef unless LJ::isu( $u ) && $cu;
+
+    # get their invite to make sure they have one
+    my $dbcr = LJ::get_cluster_def_reader($u);
+    return LJ::error('db') unless $dbcr;
+    my $argstr = $dbcr->selectrow_array('SELECT args FROM inviterecv WHERE userid = ? AND commid = ? ' .
+                                        'AND recvtime > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))',
+                                        undef, $u->{userid}, $cu->{userid});
+    return undef unless $argstr;
+
+    # decode to find out what they get
+    my $args = {};
+    LJ::decode_url_string($argstr, $args);
+
+    # valid invite.  let's accept it as far as the community listing us goes.
+    # 1, 0 means add comm to user's friends list, but don't auto-add P edge.
+    $u->join_community( $cu, 1, 0, moderated_add => 1 ) or return undef
+        if $args->{member};
+
+    # now grant necessary abilities
+    my %edgelist = (
+        post => 'P',
+        preapprove => 'N',
+        moderate => 'M',
+        admin => 'A',
+    );
+    foreach (keys %edgelist) {
+        LJ::set_rel($cu->{userid}, $u->{userid}, $edgelist{$_}) if $args->{$_};
+    }
+
+    # now we can delete the invite and update the status on the other side
+    return LJ::error('db') unless $u->writer;
+    $u->do("DELETE FROM inviterecv WHERE userid = ? AND commid = ?",
+           undef, $u->{userid}, $cu->{userid});
+
+    return LJ::error('db') unless $cu->writer;
+    $cu->do("UPDATE invitesent SET status = 'accepted' WHERE commid = ? AND userid = ?",
+            undef, $cu->{userid}, $u->{userid});
+
+    # done
+    return 1;
+}
+
+# des: Rejects an invitation a user has received.
+# args: user rejecting invite, community the user is not joining
+# returns: 1 for success, undef if failure
+sub reject_comm_invite {
+    my ( $u, $cu ) = @_;
+    $cu = LJ::want_user( $cu );
+    return undef unless LJ::isu( $u ) && $cu;
+
+    # get their invite to make sure they have one
+    my $dbcr = LJ::get_cluster_def_reader($u);
+    return LJ::error('db') unless $dbcr;
+    my $test = $dbcr->selectrow_array('SELECT userid FROM inviterecv WHERE userid = ? AND commid = ? ' .
+                                      'AND recvtime > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))',
+                                      undef, $u->{userid}, $cu->{userid});
+    return undef unless $test;
+
+    # now just reject it
+    return LJ::error('db') unless $u->writer;
+    $u->do("DELETE FROM inviterecv WHERE userid = ? AND commid = ?",
+              undef, $u->{userid}, $cu->{userid});
+
+    return LJ::error('db') unless $cu->writer;
+    $cu->do("UPDATE invitesent SET status = 'rejected' WHERE commid = ? AND userid = ?",
+            undef, $cu->{userid}, $u->{userid});
+
+    # done
+    return 1;
+}
+
+# des: Get a list of sent invitations from the past 30 days for given comm.
+# returns: hashref of arrayrefs with keys userid, maintid, recvtime, status, args (itself
+#          a hashref of what abilities the user would be given)
+sub get_sent_invites {
+    my ( $cu)  = @_;
+    return undef unless LJ::isu( $cu );
+
+    # now hit the database for their recent invites
+    my $dbcr = LJ::get_cluster_def_reader($cu);
+    return LJ::error('db') unless $dbcr;
+    my $data = $dbcr->selectall_arrayref('SELECT userid, maintid, recvtime, status, args FROM invitesent ' .
+                                         'WHERE commid = ? AND recvtime > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))',
+                                          undef, $cu->{userid});
+
+    # now break data down into usable format for caller
+    my @res;
+    foreach my $row (@{$data || []}) {
+        my $temp = {};
+        LJ::decode_url_string($row->[4], $temp);
+        push @res, {
+            userid => $row->[0]+0,
+            maintid => $row->[1]+0,
+            recvtime => $row->[2],
+            status => $row->[3],
+            args => $temp,
+        };
+    }
+
+    # all done
+    return \@res;
+}
+
+# des: Gets a list of pending community invitations for a user.
+# returns: [ [ commid, maintainerid, time, args(url encoded) ], [ ... ], ... ] or
+#          undef if failure
+sub get_pending_invites {
+    my ( $u)  = @_;
+    return undef unless LJ::isu( $u );
+
+    # hit up database for invites and return them
+    my $dbcr = LJ::get_cluster_def_reader($u);
+    return LJ::error('db') unless $dbcr;
+    my $pending = $dbcr->selectall_arrayref('SELECT commid, maintid, recvtime, args FROM inviterecv WHERE userid = ? ' .
+                                            'AND recvtime > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))', 
+                                            undef, $u->{userid});
+    return undef if $dbcr->err;
+    return $pending;
+}
+
+# des: Revokes a list of outstanding invitations to a community.
+# args: community user object, list of userids to revoke invitations for
+# returns: 1 if success, undef if error
+sub revoke_invites {
+    my ( $cu, @uids ) = @_;
+    return undef unless LJ::isu( $cu ) && @uids;
+
+    foreach my $uid (@uids) {
+        return undef unless int($uid) > 0;
+    }
+    my $in = join(',', @uids);
+
+    return LJ::error('db') unless $cu->writer;
+    $cu->do("DELETE FROM invitesent WHERE commid = ? AND " .
+            "userid IN ($in)", undef, $cu->{userid});
+    return LJ::error('db') if $cu->err;
+
+    # remove from inviterecv also,
+    # otherwise invite cannot be resent for over 30 days
+    foreach my $uid (@uids) {
+        my $u =  LJ::want_user($uid);
+        $u->do("DELETE FROM inviterecv WHERE userid = ? AND " .
+               "commid = ?", undef, $uid, $cu->{userid});
+    }
+
+    # success
+    return 1;
+}
+
+# des: Makes a user leave a community.  Takes care of all [special[reluserdefs]] and friend stuff.
+# args: u doing the leaving, comm being left, unwatch (boolean)
+# returns: 1 if success, undef if error of some sort (cu not a comm, u not in
+#          comm, db error, etc)
+sub leave_community {
+    my ( $u, $cu, $unwatch ) = @_;
+    $cu = LJ::want_user( $cu );
+
+    return LJ::error( 'comm_not_found' ) unless LJ::isu( $u ) && $cu;
+    return LJ::error( 'comm_not_comm' ) unless $cu->is_community;
+
+    # remove community membership
+    return undef
+        unless $u->remove_edge( $cu, member => {} );
+
+    # clear edges that effect this relationship
+    foreach my $edge (qw(P N A M)) {
+        LJ::clear_rel($cu->{userid}, $u->{userid}, $edge);
+    }
+
+    # unwatch user -> comm?
+    return 1 unless $unwatch;
+    $u->remove_edge( $cu, watch => {} );
+
+    # don't care if we failed the removal of comm from user's friends list...
+    return 1;
+}
+
+# name: LJ::join_community
+# des: Makes a user join a community.  Takes care of all [special[reluserdefs]] and watch stuff.
+# args: u joining, u of comm, watch?, noauto?
+# des-watch: 1 to add this comm to user's watch list, else not
+# des-noauto: if defined, 1 adds P edge, 0 does not; else, base on community postlevel
+# returns: 1 if success, undef if error of some sort (ucommid not a comm, uuserid already in
+#          comm, db error, etc)
+sub join_community {
+    my ( $u, $cu, $watch, $canpost, %opts ) = @_;
+    $cu = LJ::want_user( $cu );
+    return LJ::error( 'comm_not_found' ) unless LJ::isu( $u ) && $cu;
+    return LJ::error( 'comm_not_comm' ) unless $cu->is_community;
+
+    # try to join the community, and return if it didn't work
+    $u->add_edge( $cu, member => {
+        moderated_add => $opts{moderated_add} ? 1 : 0,
+    } ) or return LJ::error('db');
+
+    # add edges that effect this relationship... if the user sent a fourth
+    # argument, use that as a bool.  else, load commrow and use the postlevel.
+    my $addpostacc = 0;
+    # only person users can post
+    if ( $u->is_personal ) {
+        if ( defined $canpost ) {
+            $addpostacc = $canpost ? 1 : 0;
+        } else {
+            my $crow = $cu->get_community_row;
+            $addpostacc = $crow->{postlevel} eq 'members' ? 1 : 0;
+        }
+    }
+
+    LJ::set_rel( $cu->{userid}, $u->{userid}, 'P' ) if $addpostacc;
+
+    # user should watch comm?
+    return 1 unless $watch;
+
+    # don't do the work if they already watch the comm
+    return 1 if $u->watches( $cu );
+
+    # watch the comm
+    $u->add_edge( $cu, watch => {} );
+
+    # done
+    return 1;
+}
+
+# des: Gets data relevant to a community such as their membership level and posting access.
+# returns: a hashref with user, userid, name, membership, and postlevel data from the
+#          user and community tables; undef if error.
+sub get_community_row {
+    my ( $cu ) = @_;
+    return unless LJ::isu( $cu );
+
+    # hit up database
+    my $dbr = LJ::get_db_reader() or return;
+    my ($membership, $postlevel) = 
+        $dbr->selectrow_array('SELECT membership, postlevel FROM community WHERE userid=?',
+                              undef, $cu->{userid});
+    return if $dbr->err;
+    return unless $membership && $postlevel;
+
+    # return result hashref
+    my $row = {
+        user => $cu->{user},
+        userid => $cu->{userid},
+        name => $cu->{name},
+        membership => $membership,
+        postlevel => $postlevel,
+    };
+    return $row;
+}
+
+# des: Gets a list of userids for people that have requested to be added to a community
+#      but have not yet actually been approved or rejected.
+# returns: an arrayref of userids of people with pending membership requests
+sub get_pending_members {
+    my ( $cu ) = @_;
+    return unless LJ::isu( $cu );
+    
+    # database request
+    my $dbr = LJ::get_db_reader() or return;
+    my $args = $dbr->selectcol_arrayref('SELECT arg1 FROM authactions WHERE userid = ? ' .
+                                        "AND action = 'comm_join_request' AND used = 'N'",
+                                        undef, $cu->{userid}) || [];
+
+    # parse out the args
+    my @list;
+    foreach (@$args) {
+        push @list, $1+0 if $_ =~ /^targetid=(\d+)$/;
+    }
+
+    return \@list;
+}
+
+# des: Approves someone's request to join a community.  This updates the [dbtable[authactions]] table
+#      as appropriate as well as does the regular join logic.  This also generates an e-mail to
+#      be sent to the user notifying them of the acceptance.
+# args: community user object, userid to approve
+# returns: 1 on success, 0/undef on error
+sub approve_pending_member {
+    my ( $cu, $userid ) = @_;
+    my $u = LJ::want_user($userid);
+    return unless LJ::isu( $cu ) && $u;
+
+    # step 1, update authactions table
+    my $dbh = LJ::get_db_writer();
+    my $count = $dbh->do("UPDATE authactions SET used = 'Y' WHERE userid = ? AND arg1 = ?",
+                         undef, $cu->{userid}, "targetid=$u->{userid}");
+    return unless $count;
+
+    # step 2, make user join the community
+    # 1 means "add community to user's friends list"
+    return unless $u->join_community( $cu, 1, undef, moderated_add => 1 );
+
+    # step 3, email the user
+    my %params = (event => 'CommunityJoinApprove', journal => $u);
+    unless ($u->has_subscription(%params)) {
+        $u->subscribe(%params, method => 'Email');
+    }
+    LJ::Event::CommunityJoinApprove->new($u, $cu)->fire if LJ::is_enabled('esn');
+
+    $cu->memc_delete( "pendingmemct" );
+
+    return 1;
+}
+
+# des: Rejects someone's request to join a community.
+#      Updates [dbtable[authactions]] and generates an e-mail to the user.
+# args: community user object, userid to reject
+# returns: 1 on success, 0/undef on error
+sub reject_pending_member {
+    my ( $cu, $u ) = @_;
+    $u = LJ::want_user( $u );
+    return unless LJ::isu( $cu ) && $u;
+
+    # step 1, update authactions table
+    my $dbh = LJ::get_db_writer() or return;
+    my $count = $dbh->do("UPDATE authactions SET used = 'Y' WHERE userid = ? AND arg1 = ?",
+                         undef, $cu->{userid}, "targetid=$u->{userid}");
+    return unless $count;
+
+    # step 2, email the user
+    my %params = (event => 'CommunityJoinReject', journal => $u);
+    unless ($u->has_subscription(%params)) {
+        $u->subscribe(%params, method => 'Email');
+    }
+    LJ::Event::CommunityJoinReject->new($u, $cu)->fire if LJ::is_enabled('esn');
+
+    $cu->memc_delete( "pendingmemct" );
+
+    return 1;
+}
+
+# des: Registers an authaction to add a user to a
+#      community and sends an approval email to the maintainers
+# returns: Hashref; output of LJ::register_authaction()
+#          includes datecreate of old row if no new row was created
+# args: comm user object, user object to add
+sub comm_join_request {
+    my ( $comm, $u ) = @_;
+    return undef unless LJ::isu( $comm ) && LJ::isu( $u );
+
+    my $arg = "targetid=" . $u->id;
+    my $dbh = LJ::get_db_writer() or return undef;
+
+    # check for duplicates within the same hour (to prevent spamming)
+    my $oldaa = $dbh->selectrow_hashref("SELECT aaid, authcode, datecreate FROM authactions " .
+                                        "WHERE userid=? AND arg1=? " .
+                                        "AND action='comm_join_request' AND used='N' " .
+                                        "AND NOW() < datecreate + INTERVAL 1 HOUR " .
+                                        "ORDER BY 1 DESC LIMIT 1",
+                                        undef, $comm->id, $arg);
+
+    return $oldaa if $oldaa;
+
+    # insert authactions row
+    my $aa = LJ::register_authaction($comm->id, 'comm_join_request', $arg);
+    return undef unless $aa;
+
+    # if there are older duplicates, invalidate any existing unused authactions of this type
+    $dbh->do("UPDATE authactions SET used='Y' WHERE userid=? AND aaid<>? AND arg1=? " .
+             "AND action='comm_invite' AND used='N'",
+             undef, $comm->id, $aa->{'aaid'}, $arg);
+
+    # get maintainers of community
+    my $adminids = LJ::load_rel_user($comm->{userid}, 'A') || [];
+    my $admins = LJ::load_userids(@$adminids);
+
+    # now prepare the emails
+    foreach my $au (values %$admins) {
+        next unless $au && !$au->is_expunged;
+
+        # unless it's a hyphen, we need to migrate
+        my $prop = $au->prop("opt_communityjoinemail");
+        if ($prop ne "-") {
+            if ($prop ne "N") {
+                my %params = (event => 'CommunityJoinRequest', journal => $au);
+                unless ($au->has_subscription(%params)) {
+                    $au->subscribe(%params, method => $_) foreach qw(Inbox Email);
+                }
+            }
+
+            $au->set_prop("opt_communityjoinemail", "-");
+        }
+
+        LJ::Event::CommunityJoinRequest->new($au, $u, $comm)->fire;
+    }
+
+    $comm->memc_delete( 'pendingmemct' );
+
+    return $aa;
+}
+
+# Get membership and posting level settings for a community
+sub get_comm_settings {
+    my ( $c ) = @_;
+    return undef unless LJ::isu( $c );
+
+    my $cid = $c->userid;
+    my ($membership, $postlevel);
+    my $memkey = [ $cid, "commsettings:$cid" ];
+
+    my $memval = LJ::MemCache::get($memkey);
+    ( $membership, $postlevel ) = @$memval if $memval;
+    return ( $membership, $postlevel )
+        if ( $membership && $postlevel );
+
+    my $dbr = LJ::get_db_reader() or return undef;
+    ( $membership, $postlevel ) =
+        $dbr->selectrow_array("SELECT membership, postlevel FROM community WHERE userid=?", undef, $cid);
+
+    LJ::MemCache::set($memkey, [$membership,$postlevel] ) if ( $membership && $postlevel );
+
+    return ($membership, $postlevel);
+}
+
+# Set membership and posting level settings for a community
+sub set_comm_settings {
+    my ( $c, $u, $opts ) = @_;
+
+    die "Invalid users passed to set_comm_settings"
+        unless LJ::isu( $c ) && LJ::isu( $u );
+
+    die "User cannot modify this community"
+        unless $u->can_manage_other( $c );
+
+    die "Membership and posting levels are not available"
+        unless $opts->{membership} && $opts->{postlevel};
+
+    my $cid = $c->userid;
+
+    my $dbh = LJ::get_db_writer();
+    $dbh->do("REPLACE INTO community (userid, membership, postlevel) VALUES (?,?,?)" , undef, $cid, $opts->{membership}, $opts->{postlevel});
+
+    my $memkey = [ $cid, "commsettings:$cid" ];
+    LJ::MemCache::delete($memkey);
+
+    return;
+}
+
+sub maintainer_linkbar {
+    my ( $comm, $page ) = @_;
+    die "Invalid arguments passed to maintainer_linkbar"
+        unless LJ::isu( $comm ) and defined $page;
+
+    my $username = $comm->user;
+    my @links;
+
+    if ( LJ::Hooks::are_hooks( 'community_manage_link_info' ) ) {
+        my %manage_link_info = LJ::Hooks::run_hook( 'community_manage_link_info', $username );
+        if (keys %manage_link_info) {
+            push @links, $page eq "account" ?
+                "<strong>$manage_link_info{text}</strong>" :
+                "<a href='$manage_link_info{url}'>$manage_link_info{text}</a>";
+        }
+    }
+
+    push @links, (
+        $page eq "profile" ?
+            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.actinfo2') . "</strong>" :
+            "<a href='$LJ::SITEROOT/manage/profile/?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.actinfo2') . "</a>",
+        $page eq "customize" ?
+            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.customize2') . "</strong>" :
+            "<a href='$LJ::SITEROOT/customize/?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.customize2') . "</a>",
+        $page eq "settings" ?
+            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.actsettings2') . "</strong>" :
+            "<a href='$LJ::SITEROOT/community/settings?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.actsettings2') . "</a>",
+        $page eq "invites" ?
+            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.actinvites') . "</strong>" :
+            "<a href='$LJ::SITEROOT/community/sentinvites?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.actinvites') . "</a>",
+        $page eq "members" ?
+            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.actmembers2') . "</strong>" :
+            "<a href='$LJ::SITEROOT/community/members?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.actmembers2') . "</a>",
+        $page eq "queue" ?
+            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.queue') . "</strong>" :
+            "<a href='$LJ::SITEROOT/community/moderate?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.queue' ) . "</a>",
+
+    );
+
+    my $ret .= "<strong>" . LJ::Lang::ml('/community/manage.bml.managelinks', { user => $comm->ljuser_display }) . "</strong> ";
+    $ret .= join(" | ", @links);
+
+    return "<p style='margin-bottom: 20px;'>$ret</p>";
+}
+
+sub get_mod_queue_count {
+    my ( $cu ) = @_;
+    return 0 unless LJ::isu( $cu ) && $cu->is_community;
+
+    my $mqcount = $cu->memc_get( 'mqcount' );
+    return $mqcount if defined $mqcount;
+
+    # if it's not in memcache, hit the db
+    my $dbr = LJ::get_cluster_reader( $cu );
+    my $sql = "SELECT COUNT(*) FROM modlog WHERE journalid=" . $cu->id;
+    $mqcount = $dbr->selectrow_array( $sql ) || 0;
+
+    # store in memcache for 10 minutes
+    $cu->memc_set( 'mqcount' => $mqcount, 600 );
+    return $mqcount;
+}
+
+sub get_pending_members_count {
+    my ( $cu ) = @_;
+    return 0 unless LJ::isu( $cu ) && $cu->is_community;
+
+    my $pending_count = $cu->memc_get( 'pendingmemct' );
+    return $pending_count if defined $pending_count;
+
+    # seems to be doing some additional parsing, which would make this
+    # number potentially incorrect if you just do SELECT COUNT
+    # so grab the parsed list and count it
+    $pending_count = scalar @{ $cu->get_pending_members };
+    $cu->memc_set( 'pendingmemct' => $pending_count, 600 );
+
+    return $pending_count;
+}
+
+1;
diff -r a7f858ce42d3 -r 4dc0abe1fabe cgi-bin/LJ/Console/Command/Community.pm
--- a/cgi-bin/LJ/Console/Command/Community.pm	Tue Aug 31 11:16:42 2010 +0800
+++ b/cgi-bin/LJ/Console/Command/Community.pm	Tue Aug 31 12:54:57 2010 +0800
@@ -64,7 +64,7 @@ sub execute {
         if $action eq 'remove' && !$can_remove;
 
     # since adds are blocked, at this point we know we're removing the user
-    LJ::leave_community($target, $comm);
+    $target->leave_community( $comm );
     return $self->print("User " . $target->user . " removed from " . $comm->user);
 }
 
diff -r a7f858ce42d3 -r 4dc0abe1fabe cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm	Tue Aug 31 11:16:42 2010 +0800
+++ b/cgi-bin/LJ/User.pm	Tue Aug 31 12:54:57 2010 +0800
@@ -202,13 +202,14 @@ sub create_community {
     $u->set_prop("moderated", $opts{moderated}+0);
     $u->set_prop("adult_content", $opts{journal_adult_settings}) if LJ::is_enabled( 'adult_content' );
 
-    my $remote = LJ::get_remote();
-    LJ::set_rel($u, $remote, "A");  # maintainer
-    LJ::set_rel($u, $remote, "M") if $opts{moderated}; # moderator if moderated
-    LJ::join_community($remote, $u, 1, 1); # member
-
-    LJ::set_comm_settings($u, $remote, { membership => $opts{membership},
-                                         postlevel => $opts{postlevel} });
+    if ( my $remote = LJ::get_remote() ) {
+        LJ::set_rel($u, $remote, "A");  # maintainer
+        LJ::set_rel($u, $remote, "M") if $opts{moderated}; # moderator if moderated
+        $remote->join_community( $u, 1, 1 ); # member
+
+        $u->set_comm_settings( $remote, { membership => $opts{membership},
+                                          postlevel => $opts{postlevel} } );
+    }
     return $u;
 }
 
@@ -3947,7 +3948,7 @@ sub can_post_to {
 
     # let's check if this community is allowing post access to non-members
     if ( $targetu->has_open_posting ) {
-        my ( $ml, $pl ) = LJ::get_comm_settings( $targetu );
+        my ( $ml, $pl ) = $targetu->get_comm_settings;
         return 1 if $pl eq 'members';
     }
 
@@ -3981,7 +3982,7 @@ sub membership_level {
 
     return undef unless $u->is_community;
 
-    my ( $membership_level, $post_level ) = LJ::get_comm_settings( $u );
+    my ( $membership_level ) = $u->get_comm_settings;
     return $membership_level || undef;
 }
 
diff -r a7f858ce42d3 -r 4dc0abe1fabe cgi-bin/LJ/Widget/CreateAccountInviter.pm
--- a/cgi-bin/LJ/Widget/CreateAccountInviter.pm	Tue Aug 31 11:16:42 2010 +0800
+++ b/cgi-bin/LJ/Widget/CreateAccountInviter.pm	Tue Aug 31 12:54:57 2010 +0800
@@ -149,9 +149,9 @@ sub handle_post {
             if ( LJ::isu( $joinu ) ) {
                 # try to join the community
                 # if it fails and the community's moderated, send a join request and watch it
-                unless ( LJ::join_community( $u, $joinu, 1 ) ) {
+                unless ( $u->join_community( $joinu, 1 ) ) {
                     if ( $joinu->is_moderated_membership ) {
-                        LJ::comm_join_request( $joinu, $u );
+                        $joinu->comm_join_request( $u );
                         $u->add_edge( $joinu, watch => {} );
                     }
                 }
diff -r a7f858ce42d3 -r 4dc0abe1fabe cgi-bin/communitylib.pl
--- a/cgi-bin/communitylib.pl	Tue Aug 31 11:16:42 2010 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,694 +0,0 @@
-#!/usr/bin/perl
-# This code was forked from the LiveJournal project owned and operated
-# by Live Journal, Inc. The code has been modified and expanded by
-# Dreamwidth Studios, LLC. These files were originally licensed under
-# the terms of the license supplied by Live Journal, Inc, which can
-# currently be found at:
-#
-# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
-#
-# In accordance with the original license, this code and all its
-# modifications are provided under the GNU General Public License.
-# A copy of that license can be found in the LICENSE file included as
-# part of this distribution.
-
-package LJ;
-
-use strict;
-use LJ::Event::CommunityInvite;
-use LJ::Event::CommunityJoinRequest;
-use LJ::Event::CommunityJoinApprove;
-use LJ::Event::CommunityJoinReject;
-
-# <LJFUNC>
-# name: LJ::get_sent_invites
-# des: Get a list of sent invitations from the past 30 days.
-# args: cuserid
-# des-cuserid: a userid or u object of the community to get sent invitations for
-# returns: hashref of arrayrefs with keys userid, maintid, recvtime, status, args (itself
-#          a hashref of what abilities the user would be given)
-# </LJFUNC>
-sub get_sent_invites {
-    my $cu = shift;
-    $cu = LJ::want_user($cu);
-    return undef unless $cu;
-
-    # now hit the database for their recent invites
-    my $dbcr = LJ::get_cluster_def_reader($cu);
-    return LJ::error('db') unless $dbcr;
-    my $data = $dbcr->selectall_arrayref('SELECT userid, maintid, recvtime, status, args FROM invitesent ' .
-                                         'WHERE commid = ? AND recvtime > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))',
-                                          undef, $cu->{userid});
-
-    # now break data down into usable format for caller
-    my @res;
-    foreach my $row (@{$data || []}) {
-        my $temp = {};
-        LJ::decode_url_string($row->[4], $temp);
-        push @res, {
-            userid => $row->[0]+0,
-            maintid => $row->[1]+0,
-            recvtime => $row->[2],
-            status => $row->[3],
-            args => $temp,
-        };
-    }
-
-    # all done
-    return \@res;    
-}
-
-# <LJFUNC>
-# name: LJ::send_comm_invite
-# des: Sends an invitation to a user to join a community with the passed abilities.
-# args: uuserid, cuserid, muserid, attrs
-# des-uuserid: a userid or u object of the user to invite.
-# des-cuserid: a userid or u object of the community to invite the user to.
-# des-muserid: a userid or u object of the maintainer doing the inviting.
-# des-attrs: a hashref of abilities this user should have (e.g. member, post, unmoderated, ...)
-# returns: 1 for success, undef if failure
-# </LJFUNC>
-sub send_comm_invite {
-    my ($u, $cu, $mu, $attrs) = @_;
-    $u = LJ::want_user($u);
-    $cu = LJ::want_user($cu);
-    $mu = LJ::want_user($mu);
-    return undef unless $u && $cu && $mu;
-
-    # step 1: if the user has banned the community, don't accept the invite
-    return LJ::error('comm_user_has_banned') if $u->has_banned( $cu );
-
-    # step 2: lazily clean out old community invites.
-    return LJ::error('db') unless $u->writer;
-    $u->do('DELETE FROM inviterecv WHERE userid = ? AND ' .
-           'recvtime < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))',
-           undef, $u->{userid});
-
-    return LJ::error('db') unless $cu->writer;
-    $cu->do('DELETE FROM invitesent WHERE commid = ? AND ' .
-            'recvtime < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))',
-            undef, $cu->{userid});
-
-    my $dbcr = LJ::get_cluster_def_reader($u);
-    return LJ::error('db') unless $dbcr;
-    my $argstr = $dbcr->selectrow_array('SELECT args FROM inviterecv WHERE userid = ? AND commid = ?',
-                                        undef, $u->{userid}, $cu->{userid});
-
-    # step 4: exceeded outstanding invitation limit?  only if no outstanding invite
-    unless ($argstr) {
-        my $cdbcr = LJ::get_cluster_def_reader($cu);
-        return LJ::error('db') unless $cdbcr;
-        my $count = $cdbcr->selectrow_array("SELECT COUNT(*) FROM invitesent WHERE commid = ? " .
-                                            "AND userid <> ? AND status = 'outstanding'",
-                                            undef, $cu->{userid}, $u->{userid});
-
-        # for now, limit to 500 outstanding invitations per community.  if this is not enough
-        # it can be raised or put back to the old system of using community size as an indicator
-        # of how many people to allow.
-        return LJ::error('comm_invite_limit') if $count > 500;
-    }
-
-    # step 5: setup arg string as url-encoded string
-    my $newargstr = join('=1&', map { LJ::eurl($_) } @$attrs) . '=1';
-
-    # step 6: branch here to update or insert
-    if ($argstr) {
-        # merely an update, so just do it quietly
-        $u->do("UPDATE inviterecv SET args = ? WHERE userid = ? AND commid = ?",
-               undef, $newargstr, $u->{userid}, $cu->{userid});
-
-        $cu->do("UPDATE invitesent SET args = ?, status = 'outstanding' WHERE userid = ? AND commid = ?",
-                undef, $newargstr, $cu->{userid}, $u->{userid});
-    } else {
-         # insert new data, as this is a new invite
-         $u->do("INSERT INTO inviterecv VALUES (?, ?, ?, UNIX_TIMESTAMP(), ?)",
-                undef, $u->{userid}, $cu->{userid}, $mu->{userid}, $newargstr);
-
-         $cu->do("REPLACE INTO invitesent VALUES (?, ?, ?, UNIX_TIMESTAMP(), 'outstanding', ?)",
-                 undef, $cu->{userid}, $u->{userid}, $mu->{userid}, $newargstr);
-    }
-
-    # Fire community invite event
-    LJ::Event::CommunityInvite->new($u, $mu, $cu)->fire if LJ::is_enabled('esn');
-
-    # step 7: error check database work
-    return LJ::error('db') if $u->err || $cu->err;
-
-    # success
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::accept_comm_invite
-# des: Accepts an invitation a user has received.  This does all the work to make the
-#      user join the community as well as sets up privileges.
-# args: uuserid, cuserid
-# des-uuserid: a userid or u object of the user to get pending invites for
-# des-cuserid: a userid or u object of the community to reject the invitation from
-# returns: 1 for success, undef if failure
-# </LJFUNC>
-sub accept_comm_invite {
-    my ($u, $cu) = @_;
-    $u = LJ::want_user($u);
-    $cu = LJ::want_user($cu);
-    return undef unless $u && $cu;
-
-    # get their invite to make sure they have one
-    my $dbcr = LJ::get_cluster_def_reader($u);
-    return LJ::error('db') unless $dbcr;
-    my $argstr = $dbcr->selectrow_array('SELECT args FROM inviterecv WHERE userid = ? AND commid = ? ' .
-                                        'AND recvtime > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))',
-                                        undef, $u->{userid}, $cu->{userid});
-    return undef unless $argstr;
-
-    # decode to find out what they get
-    my $args = {};
-    LJ::decode_url_string($argstr, $args);
-
-    # valid invite.  let's accept it as far as the community listing us goes.
-    # 1, 0 means add comm to user's friends list, but don't auto-add P edge.
-    LJ::join_community( $u, $cu, 1, 0, moderated_add => 1 ) or return undef
-        if $args->{member};
-
-    # now grant necessary abilities
-    my %edgelist = (
-        post => 'P',
-        preapprove => 'N',
-        moderate => 'M',
-        admin => 'A',
-    );
-    foreach (keys %edgelist) {
-        LJ::set_rel($cu->{userid}, $u->{userid}, $edgelist{$_}) if $args->{$_};
-    }
-
-    # now we can delete the invite and update the status on the other side
-    return LJ::error('db') unless $u->writer;
-    $u->do("DELETE FROM inviterecv WHERE userid = ? AND commid = ?",
-           undef, $u->{userid}, $cu->{userid});
-
-    return LJ::error('db') unless $cu->writer;
-    $cu->do("UPDATE invitesent SET status = 'accepted' WHERE commid = ? AND userid = ?",
-            undef, $cu->{userid}, $u->{userid});
-
-    # done
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::reject_comm_invite
-# des: Rejects an invitation a user has received.
-# args: uuserid, cuserid
-# des-uuserid: a userid or u object of the user to get pending invites for.
-# des-cuserid: a userid or u object of the community to reject the invitation from
-# returns: 1 for success, undef if failure
-# </LJFUNC>
-sub reject_comm_invite {
-    my ($u, $cu) = @_;
-    $u = LJ::want_user($u);
-    $cu = LJ::want_user($cu);
-    return undef unless $u && $cu;
-
-    # get their invite to make sure they have one
-    my $dbcr = LJ::get_cluster_def_reader($u);
-    return LJ::error('db') unless $dbcr;
-    my $test = $dbcr->selectrow_array('SELECT userid FROM inviterecv WHERE userid = ? AND commid = ? ' .
-                                      'AND recvtime > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))',
-                                      undef, $u->{userid}, $cu->{userid});
-    return undef unless $test;
-
-    # now just reject it
-    return LJ::error('db') unless $u->writer;
-    $u->do("DELETE FROM inviterecv WHERE userid = ? AND commid = ?",
-              undef, $u->{userid}, $cu->{userid});
-
-    return LJ::error('db') unless $cu->writer;
-    $cu->do("UPDATE invitesent SET status = 'rejected' WHERE commid = ? AND userid = ?",
-            undef, $cu->{userid}, $u->{userid});
-
-    # done
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::get_pending_invites
-# des: Gets a list of pending invitations for a user to join a community.
-# args: uuserid
-# des-uuserid: a userid or u object of the user to get pending invites for.
-# returns: [ [ commid, maintainerid, time, args(url encoded) ], [ ... ], ... ] or
-#          undef if failure
-# </LJFUNC>
-sub get_pending_invites {
-    my $u = shift;
-    $u = LJ::want_user($u);
-    return undef unless $u;
-
-    # hit up database for invites and return them
-    my $dbcr = LJ::get_cluster_def_reader($u);
-    return LJ::error('db') unless $dbcr;
-    my $pending = $dbcr->selectall_arrayref('SELECT commid, maintid, recvtime, args FROM inviterecv WHERE userid = ? ' .
-                                            'AND recvtime > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 30 DAY))', 
-                                            undef, $u->{userid});
-    return undef if $dbcr->err;
-    return $pending;
-}
-
-# <LJFUNC>
-# name: LJ::revoke_invites
-# des: Revokes a list of outstanding invitations to a community.
-# args: cuserid, userids
-# des-cuserid: a userid or u object of the community.
-# des-ruserids: userids to revoke invitations from.
-# returns: 1 if success, undef if error
-# </LJFUNC>
-sub revoke_invites {
-    my $cu = shift;
-    my @uids = @_;
-    $cu = LJ::want_user($cu);
-    return undef unless ($cu && @uids);
-
-    foreach my $uid (@uids) {
-        return undef unless int($uid) > 0;
-    }
-    my $in = join(',', @uids);
-
-    return LJ::error('db') unless $cu->writer;
-    $cu->do("DELETE FROM invitesent WHERE commid = ? AND " .
-            "userid IN ($in)", undef, $cu->{userid});
-    return LJ::error('db') if $cu->err;
-
-    # remove from inviterecv also,
-    # otherwise invite cannot be resent for over 30 days
-    foreach my $uid (@uids) {
-        my $u =  LJ::want_user($uid);
-        $u->do("DELETE FROM inviterecv WHERE userid = ? AND " .
-               "commid = ?", undef, $uid, $cu->{userid});
-    }
-
-    # success
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::leave_community
-# des: Makes a user leave a community.  Takes care of all [special[reluserdefs]] and friend stuff.
-# args: uuserid, ucommid, defriend
-# des-uuserid: a userid or u object of the user doing the leaving.
-# des-ucommid: a userid or u object of the community being left.
-# des-defriend: remove comm from user's friends list.
-# returns: 1 if success, undef if error of some sort (ucommid not a comm, uuserid not in
-#          comm, db error, etc)
-# </LJFUNC>
-sub leave_community {
-    my ($uuid, $ucid, $defriend) = @_;
-    my $u = LJ::want_user($uuid);
-    my $cu = LJ::want_user($ucid);
-    $defriend = $defriend ? 1 : 0;
-    return LJ::error( 'comm_not_found' ) unless $u && $cu;
-    return LJ::error( 'comm_not_comm' ) unless $cu->is_community;
-
-    # remove community membership
-    return undef
-        unless $u->remove_edge( $cu, member => {} );
-
-    # clear edges that effect this relationship
-    foreach my $edge (qw(P N A M)) {
-        LJ::clear_rel($cu->{userid}, $u->{userid}, $edge);
-    }
-
-    # defriend user -> comm?
-    return 1 unless $defriend;
-    $u->remove_edge( $cu, watch => {} );
-
-    # don't care if we failed the removal of comm from user's friends list...
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::join_community
-# des: Makes a user join a community.  Takes care of all [special[reluserdefs]] and watch stuff.
-# args: uuserid, ucommid, watch?, noauto?
-# des-uuserid: a userid or u object of the user doing the joining
-# des-ucommid: a userid or u object of the community being joined
-# des-watch: 1 to add this comm to user's watch list, else not
-# des-noauto: if defined, 1 adds P edge, 0 does not; else, base on community postlevel
-# returns: 1 if success, undef if error of some sort (ucommid not a comm, uuserid already in
-#          comm, db error, etc)
-# </LJFUNC>
-sub join_community {
-    my ( $uuid, $ucid, $watch, $canpost, %opts ) = @_;
-    my $u = LJ::want_user($uuid);
-    my $cu = LJ::want_user($ucid);
-    $watch = $watch ? 1 : 0;
-    return LJ::error( 'comm_not_found' ) unless $u && $cu;
-    return LJ::error( 'comm_not_comm' ) unless $cu->is_community;
-
-    # try to join the community, and return if it didn't work
-    $u->add_edge( $cu, member => {
-        moderated_add => $opts{moderated_add} ? 1 : 0,
-    } ) or return LJ::error('db');
-
-    # add edges that effect this relationship... if the user sent a fourth
-    # argument, use that as a bool.  else, load commrow and use the postlevel.
-    my $addpostacc = 0;
-    # only person users can post
-    if ( $u->is_personal ) {
-        if ( defined $canpost ) {
-            $addpostacc = $canpost ? 1 : 0;
-        } else {
-            my $crow = LJ::get_community_row( $cu );
-            $addpostacc = $crow->{postlevel} eq 'members' ? 1 : 0;
-        }
-    }
-
-    LJ::set_rel( $cu->{userid}, $u->{userid}, 'P' ) if $addpostacc;
-
-    # user should watch comm?
-    return 1 unless $watch;
-
-    # don't do the work if they already watch the comm
-    return 1 if $u->watches( $cu );
-
-    # watch the comm
-    $u->add_edge( $cu, watch => {} );
-
-    # done
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::get_community_row
-# des: Gets data relevant to a community such as their membership level and posting access.
-# args: ucommid
-# des-ucommid: a userid or u object of the community
-# returns: a hashref with user, userid, name, membership, and postlevel data from the
-#          user and community tables; undef if error.
-# </LJFUNC>
-sub get_community_row {
-    my $ucid = shift;
-    my $cu = LJ::want_user($ucid);
-    return unless $cu;
-
-    # hit up database
-    my $dbr = LJ::get_db_reader();
-    my ($membership, $postlevel) = 
-        $dbr->selectrow_array('SELECT membership, postlevel FROM community WHERE userid=?',
-                              undef, $cu->{userid});
-    return if $dbr->err;
-    return unless $membership && $postlevel;
-
-    # return result hashref
-    my $row = {
-        user => $cu->{user},
-        userid => $cu->{userid},
-        name => $cu->{name},
-        membership => $membership,
-        postlevel => $postlevel,
-    };
-    return $row;
-}
-
-# <LJFUNC>
-# name: LJ::get_pending_members
-# des: Gets a list of userids for people that have requested to be added to a community
-#      but have not yet actually been approved or rejected.
-# args: comm
-# des-comm: a userid or u object of the community to get pending members of
-# returns: an arrayref of userids of people with pending membership requests
-# </LJFUNC>
-sub get_pending_members {
-    my $comm = shift;
-    my $cu = LJ::want_user($comm);
-    
-    # database request
-    my $dbr = LJ::get_db_reader();
-    my $args = $dbr->selectcol_arrayref('SELECT arg1 FROM authactions WHERE userid = ? ' .
-                                        "AND action = 'comm_join_request' AND used = 'N'",
-                                        undef, $cu->{userid}) || [];
-
-    # parse out the args
-    my @list;
-    foreach (@$args) {
-        push @list, $1+0 if $_ =~ /^targetid=(\d+)$/;
-    }
-
-    return \@list;
-}
-
-# <LJFUNC>
-# name: LJ::approve_pending_member
-# des: Approves someone's request to join a community.  This updates the [dbtable[authactions]] table
-#      as appropriate as well as does the regular join logic.  This also generates an e-mail to
-#      be sent to the user notifying them of the acceptance.
-# args: commid, userid
-# des-commid: userid of the community
-# des-userid: userid of the user doing the join
-# returns: 1 on success, 0/undef on error
-# </LJFUNC>
-sub approve_pending_member {
-    my ($commid, $userid) = @_;
-    my $cu = LJ::want_user($commid);
-    my $u = LJ::want_user($userid);
-    return unless $cu && $u;
-
-    # step 1, update authactions table
-    my $dbh = LJ::get_db_writer();
-    my $count = $dbh->do("UPDATE authactions SET used = 'Y' WHERE userid = ? AND arg1 = ?",
-                         undef, $cu->{userid}, "targetid=$u->{userid}");
-    return unless $count;
-
-    # step 2, make user join the community
-    # 1 means "add community to user's friends list"
-    return unless LJ::join_community( $u, $cu, 1, undef, moderated_add => 1 );
-
-    # step 3, email the user
-    my %params = (event => 'CommunityJoinApprove', journal => $u);
-    unless ($u->has_subscription(%params)) {
-        $u->subscribe(%params, method => 'Email');
-    }
-    LJ::Event::CommunityJoinApprove->new($u, $cu)->fire if LJ::is_enabled('esn');
-
-    $cu->memc_delete( "pendingmemct" );
-
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::reject_pending_member
-# des: Rejects someone's request to join a community.
-#      Updates [dbtable[authactions]] and generates an e-mail to the user.
-# args: commid, userid
-# des-commid: userid of the community
-# des-userid: userid of the user doing the join
-# returns: 1 on success, 0/undef on error
-# </LJFUNC>
-sub reject_pending_member {
-    my ($commid, $userid) = @_;
-    my $cu = LJ::want_user($commid);
-    my $u = LJ::want_user($userid);
-    return unless $cu && $u;
-
-    # step 1, update authactions table
-    my $dbh = LJ::get_db_writer();
-    my $count = $dbh->do("UPDATE authactions SET used = 'Y' WHERE userid = ? AND arg1 = ?",
-                         undef, $cu->{userid}, "targetid=$u->{userid}");
-    return unless $count;
-
-    # step 2, email the user
-    my %params = (event => 'CommunityJoinReject', journal => $u);
-    unless ($u->has_subscription(%params)) {
-        $u->subscribe(%params, method => 'Email');
-    }
-    LJ::Event::CommunityJoinReject->new($u, $cu)->fire if LJ::is_enabled('esn');
-
-    $cu->memc_delete( "pendingmemct" );
-
-    return 1;
-}
-
-# <LJFUNC>
-# name: LJ::comm_join_request
-# des: Registers an authaction to add a user to a
-#      community and sends an approval email to the maintainers
-# returns: Hashref; output of LJ::register_authaction()
-#          includes datecreate of old row if no new row was created
-# args: comm, u
-# des-comm: Community user object
-# des-u: User object to add to community
-# </LJFUNC>
-sub comm_join_request {
-    my ($comm, $u) = @_;
-    return undef unless ref $comm && ref $u;
-
-    my $arg = "targetid=" . $u->id;
-    my $dbh = LJ::get_db_writer();
-
-    # check for duplicates within the same hour (to prevent spamming)
-    my $oldaa = $dbh->selectrow_hashref("SELECT aaid, authcode, datecreate FROM authactions " .
-                                        "WHERE userid=? AND arg1=? " .
-                                        "AND action='comm_join_request' AND used='N' " .
-                                        "AND NOW() < datecreate + INTERVAL 1 HOUR " .
-                                        "ORDER BY 1 DESC LIMIT 1",
-                                        undef, $comm->id, $arg);
-
-    return $oldaa if $oldaa;
-
-    # insert authactions row
-    my $aa = LJ::register_authaction($comm->id, 'comm_join_request', $arg);
-    return undef unless $aa;
-
-    # if there are older duplicates, invalidate any existing unused authactions of this type
-    $dbh->do("UPDATE authactions SET used='Y' WHERE userid=? AND aaid<>? AND arg1=? " .
-             "AND action='comm_invite' AND used='N'",
-             undef, $comm->id, $aa->{'aaid'}, $arg);
-
-    # get maintainers of community
-    my $adminids = LJ::load_rel_user($comm->{userid}, 'A') || [];
-    my $admins = LJ::load_userids(@$adminids);
-
-    # now prepare the emails
-    foreach my $au (values %$admins) {
-        next unless $au && !$au->is_expunged;
-
-        # unless it's a hyphen, we need to migrate
-        my $prop = $au->prop("opt_communityjoinemail");
-        if ($prop ne "-") {
-            if ($prop ne "N") {
-                my %params = (event => 'CommunityJoinRequest', journal => $au);
-                unless ($au->has_subscription(%params)) {
-                    $au->subscribe(%params, method => $_) foreach qw(Inbox Email);
-                }
-            }
-
-            $au->set_prop("opt_communityjoinemail", "-");
-        }
-
-        LJ::Event::CommunityJoinRequest->new($au, $u, $comm)->fire;
-    }
-
-    $comm->memc_delete( 'pendingmemct' );
-
-    return $aa;
-}
-
-sub maintainer_linkbar {
-    my $comm = shift;
-    my $page = shift;
-
-    my $username = $comm->user;
-    my @links;
-
-    my %manage_link_info = LJ::Hooks::run_hook('community_manage_link_info', $username);
-    if (keys %manage_link_info) {
-        push @links, $page eq "account" ?
-            "<strong>$manage_link_info{text}</strong>" :
-            "<a href='$manage_link_info{url}'>$manage_link_info{text}</a>";
-    }
-
-    push @links, (
-        $page eq "profile" ?
-            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.actinfo2') . "</strong>" :
-            "<a href='$LJ::SITEROOT/manage/profile/?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.actinfo2') . "</a>",
-        $page eq "customize" ?
-            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.customize2') . "</strong>" :
-            "<a href='$LJ::SITEROOT/customize/?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.customize2') . "</a>",
-        $page eq "settings" ?
-            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.actsettings2') . "</strong>" :
-            "<a href='$LJ::SITEROOT/community/settings?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.actsettings2') . "</a>",
-        $page eq "invites" ?
-            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.actinvites') . "</strong>" :
-            "<a href='$LJ::SITEROOT/community/sentinvites?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.actinvites') . "</a>",
-        $page eq "members" ?
-            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.actmembers2') . "</strong>" :
-            "<a href='$LJ::SITEROOT/community/members?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.actmembers2') . "</a>",
-        $page eq "queue" ?
-            "<strong>" . LJ::Lang::ml('/community/manage.bml.commlist.queue') . "</strong>" :
-            "<a href='$LJ::SITEROOT/community/moderate?authas=$username'>" . LJ::Lang::ml('/community/manage.bml.commlist.queue' ) . "</a>",
-
-    );
-
-    my $ret .= "<strong>" . LJ::Lang::ml('/community/manage.bml.managelinks', { user => $comm->ljuser_display }) . "</strong> ";
-    $ret .= join(" | ", @links);
-
-    return "<p style='margin-bottom: 20px;'>$ret</p>";
-}
-
-# Get membership and posting level settings for a community
-sub get_comm_settings {
-    my $c = shift;
-
-    my $cid = $c->{userid};
-    my ($membership, $postlevel);
-    my $memkey = [ $cid, "commsettings:$cid" ];
-
-    my $memval = LJ::MemCache::get($memkey);
-    ($membership, $postlevel) = @$memval if ($memval);
-    return ($membership, $postlevel)
-        if ( $membership && $postlevel );
-
-    my $dbr = LJ::get_db_reader();
-    ($membership, $postlevel) =
-        $dbr->selectrow_array("SELECT membership, postlevel FROM community WHERE userid=?", undef, $cid);
-
-    LJ::MemCache::set($memkey, [$membership,$postlevel] ) if ( $membership && $postlevel );
-
-    return ($membership, $postlevel);
-}
-
-# Set membership and posting level settings for a community
-sub set_comm_settings {
-    my ( $c, $u, $opts ) = @_;
-
-    die "Invalid users passed to set_comm_settings"
-        unless LJ::isu( $c ) && LJ::isu( $u );
-
-    die "User cannot modify this community"
-        unless $u->can_manage_other( $c );
-
-    die "Membership and posting levels are not available"
-        unless $opts->{membership} && $opts->{postlevel};
-
-    my $cid = $c->userid;
-
-    my $dbh = LJ::get_db_writer();
-    $dbh->do("REPLACE INTO community (userid, membership, postlevel) VALUES (?,?,?)" , undef, $cid, $opts->{membership}, $opts->{postlevel});
-
-    my $memkey = [ $cid, "commsettings:$cid" ];
-    LJ::MemCache::delete($memkey);
-
-    return;
-}
-
-sub get_mod_queue_count {
-    my $cu = LJ::want_user( shift );
-    return 0 unless $cu->is_community;
-
-    my $mqcount = $cu->memc_get( 'mqcount' );
-    return $mqcount if defined $mqcount;
-
-    # if it's not in memcache, hit the db
-    my $dbr = LJ::get_cluster_reader( $cu );
-    my $sql = "SELECT COUNT(*) FROM modlog WHERE journalid=" . $cu->id;
-    $mqcount = $dbr->selectrow_array( $sql ) || 0;
-
-    # store in memcache for 10 minutes
-    $cu->memc_set( 'mqcount' => $mqcount, 600 );
-    return $mqcount;
-}
-
-sub get_pending_members_count {
-    my $cu = LJ::want_user( shift );
-    return 0 unless $cu->is_community;
-
-    my $pending_count = $cu->memc_get( 'pendingmemct' );
-    return $pending_count if defined $pending_count;
-
-    # seems to be doing some additional parsing, which would make this
-    # number potentially incorrect if you just do SELECT COUNT
-    # so grab the parsed list and count it
-    $pending_count = scalar @{ LJ::get_pending_members( $cu ) };
-    $cu->memc_set( 'pendingmemct' => $pending_count, 600 );
-
-    return $pending_count;
-}
-
-1;
diff -r a7f858ce42d3 -r 4dc0abe1fabe cgi-bin/modperl_subs.pl
--- a/cgi-bin/modperl_subs.pl	Tue Aug 31 11:16:42 2010 +0800
+++ b/cgi-bin/modperl_subs.pl	Tue Aug 31 12:54:57 2010 +0800
@@ -92,7 +92,7 @@ require "ljmemories.pl";
 require "ljmemories.pl";
 require "ljmail.pl";
 require "sysban.pl";
-require "communitylib.pl";
+use LJ::Community;
 use LJ::Tags;
 require "ljemailgateway-web.pl";
 use LJ::Customize;
diff -r a7f858ce42d3 -r 4dc0abe1fabe cgi-bin/weblib.pl
--- a/cgi-bin/weblib.pl	Tue Aug 31 11:16:42 2010 +0800
+++ b/cgi-bin/weblib.pl	Tue Aug 31 12:54:57 2010 +0800
@@ -2658,7 +2658,7 @@ sub control_strip
                     if $haspostingaccess;
 
                 if ( $journal->prop( 'moderated' ) ) {
-                    $ret .= "$links{queue} [" . LJ::get_mod_queue_count( $journal ) . "]&nbsp;&nbsp;";
+                    $ret .= "$links{queue} [" . $journal->get_mod_queue_count . "]&nbsp;&nbsp;";
                 } else {
                     $ret .= "$links{edit_community_profile}&nbsp;&nbsp;";
                 }
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/approve.bml
--- a/htdocs/approve.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/approve.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -43,20 +43,21 @@ body<=
         my $dbh = LJ::get_db_writer();
 
         # get user we're adding
-        my $targetid = $arg->{targetid};
-        return LJ::bad_input($ML{'.error.internerr.invalidaction'}) unless $targetid;
+        my $targetu = LJ::load_userid( $arg->{targetid} );
+        return LJ::bad_input($ML{'.error.internerr.invalidaction'}) unless $targetu;
 
         # add to community
         return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.approving'} p?>"
-            unless LJ::approve_pending_member($aa->{userid}, $targetid);
+            unless my $cu = LJ::load_userid( $aa->{userid} );
+        return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.approving'} p?>"
+            unless $cu->approve_pending_member( $targetu );
 
         # return success
-        my $commname = LJ::get_username($aa->{userid});
-        my $username = LJ::get_username($targetid);
+        my $commname = $cu->user;
         return "<?h1 $ML{'.comm.success'} h1?>".
                '<?p ' . BML::ml('.commjoin.text', {
-                           user => LJ::ljuser($username),
-                           comm => LJ::ljuser($commname),
+                           user => $targetu->ljuser_display,
+                           comm => $cu->ljuser_display,
                            aopts => "href=\"$LJ::SITEROOT/community/members.bml?authas=$commname\"",
                         }) . ' p?>';
     }
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/community/join.bml
--- a/htdocs/community/join.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/community/join.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -74,7 +74,7 @@ body<=
         return "<?h1 $ML{'Error'} h1?><?p $adult_err p?>";
     }
 
-    my $ci = LJ::get_community_row($cu);
+    my $ci = $cu->get_community_row;
     my $cuserid = $ci->{'userid'};
 
     LJ::text_out(\$ci->{'name'});
@@ -111,14 +111,14 @@ body<=
         # can members join this community openly?
         if ($ci->{membership} ne 'open') {
             # hit up the maintainers to let them know a join was requested
-            LJ::comm_join_request($cu, $remote);
+            $cu->comm_join_request( $remote );
             $ret = "<?h1 $ML{'.reqsubmitted.title'} h1?><?p $ML{'.reqsubmitted.body'} p?> ";
             $ret .= $next_links;
             return $ret;
           }
 
         # join community
-        my $joined = LJ::join_community($remote, $cu);
+        my $joined = $remote->join_community( $cu );
         unless ($joined) {
             return "<?h1 $ML{'error'} h1?>"
                    . "<?p " . LJ::last_error() . " p?>";
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/community/leave.bml
--- a/htdocs/community/leave.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/community/leave.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -61,7 +61,7 @@ _c?>
         if ($watching && !$memberof) {
             $remote->remove_edge( $cu, watch => {} );
         } else {
-            LJ::leave_community($remote, $cu, $FORM{removefriend});
+            $remote->leave_community( $cu, $FORM{removefriend} );
         }
 
         # success message
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/community/manage.bml
--- a/htdocs/community/manage.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/community/manage.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -66,10 +66,10 @@ body<=
            next unless $cu && ( $cu->is_visible || $cu->is_readonly );
            $names{$uid} = [ $cu->{user}, $cu->{name}, -1 ];
            if ($mods{$uid}) {
-               $modcount{$uid} = $names{$uid}[2] = LJ::get_mod_queue_count( $uid );
+               $modcount{$uid} = $names{$uid}[2] = $cu->get_mod_queue_count;
            }
            if ($membership eq 'moderated') {
-               my $ids = LJ::get_pending_members($uid) || [];
+               my $ids = $cu->get_pending_members || [];
                $pending{$uid} = scalar @$ids;
            }
        }
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/community/members.bml
--- a/htdocs/community/members.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/community/members.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -70,7 +70,7 @@ body<=
         return $ret;
     }
 
-    $ret .= LJ::maintainer_linkbar($c, "members");
+    $ret .= $c->maintainer_linkbar( "members" );
 
     my @allattribs = ('member', 'post', 'preapprove', 'moderate', 'admin');
     my %attrshort = ( X => 'member', P => 'post', N => 'preapprove', M => 'moderate', A => 'admin');
@@ -168,7 +168,7 @@ body<=
             foreach my $row (@to_add) {
                 # good, let's extend an invite to this person
                 my ($target, $attrs) = @$row;
-                if (LJ::send_comm_invite($target, $c, $remote, $attrs)) {
+                if ( $target->send_comm_invite( $c, $remote, $attrs ) ) {
                     push @invited, $row;
                 } else {
                     push @fail, [ $target, LJ::last_error_code() ];
@@ -370,12 +370,12 @@ body<=
                 my $u = LJ::load_userid($id);
                 if ( $remote->equals( $u ) ) {
                     # you're allowed to add yourself as member
-                    LJ::join_community($remote, $c);
+                    $remote->join_community( $c );
                 } else {
-                    if (LJ::send_comm_invite($u, $c, $remote, [ 'member' ])) {
+                    if ( $u && $u->send_comm_invite( $c, $remote, [ 'member' ] ) ) {
                         # if it succeeded, push the reinvited information
                         push @msgs, BML::ml('.reinvited2',
-                            { user => LJ::ljuser($u),
+                            { user => $u->ljuser_display,
                               aopts => "href='$LJ::SITEROOT/manage/invites'" });
                     }
                 }
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/community/moderate.bml
--- a/htdocs/community/moderate.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/community/moderate.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -53,11 +53,11 @@ body<=
         my $modid = $POST{'modid'}+0;
 
         my $c = LJ::load_userid($cid);
-        unless ($c) {
+        my $dbcm = LJ::get_cluster_master( $c );
+        unless ( $c && $dbcm ) {
             $ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.notfound'} p?>";
             return $ret;
         }
-        my $dbcm = LJ::get_cluster_master($c);
 
         unless ( $remote->can_moderate( $c ) ) {
             $ret .= "<?h1 $ML{'Error'} h1?><?p " .
@@ -65,7 +65,7 @@ body<=
             return $ret;
         }
 
-        $ret .= LJ::maintainer_linkbar($c, "queue");
+        $ret .= $c->maintainer_linkbar( "queue" );
 
         # use dbcm to read to minimize collisions between moderators due to replication lag
         my $entry = $dbcm->selectrow_hashref("SELECT * FROM modlog WHERE journalid=? AND modid=?",
@@ -226,7 +226,8 @@ body<=
         return $ret;
     }
 
-    $ret .= LJ::maintainer_linkbar($c, "queue") unless LJ::did_post() && ($mode eq 'approve_do' || $mode eq 'reject_do');
+    $ret .= $c->maintainer_linkbar( "queue" )
+        unless LJ::did_post() && ( $mode eq 'approve_do' || $mode eq 'reject_do' );
 
     my $formauth = LJ::form_auth();
 
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/community/pending.bml
--- a/htdocs/community/pending.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/community/pending.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -56,10 +56,10 @@ body<=
         return $ret;
     }
 
-    $ret .= LJ::maintainer_linkbar($c);
+    $ret .= $c->maintainer_linkbar( "pending" );
 
     # hit up the database to find pending members
-    my $pendids = LJ::get_pending_members($c) || [];
+    my $pendids = $c->get_pending_members || [];
     my $us = LJ::load_userids(@$pendids);
 
     # nothing pending?
@@ -91,10 +91,10 @@ body<=
             }
 
             if ($POST{"pending_$id"} eq 'on' and $POST{"approve"}) {
-                LJ::approve_pending_member($cid, $id);
+                $c->approve_pending_member( $us->{$id} );
                 $added++;
             } elsif ($POST{"pending_$id"} eq 'on' and $POST{"reject"}) {
-                LJ::reject_pending_member($cid, $id);
+                $c->reject_pending_member( $us->{$id} );
                 $rejected++;
             } elsif ($POST{"pending_$id"} eq 'on' and $POST{"reject_ban"}) {
                 my $banlist = LJ::load_rel_user($c, 'B') || [];
@@ -106,7 +106,7 @@ body<=
                     LJ::Hooks::run_hooks('ban_set', $c, $us->{$id});
                     $banned++;
 
-                    LJ::reject_pending_member($cid, $id); # only in case of successful ban
+                    $c->reject_pending_member( $us->{$id} ); # only in case of successful ban
                     $rejected++; # for keeping this user in list for later reject
                 }
             }
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/community/sentinvites.bml
--- a/htdocs/community/sentinvites.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/community/sentinvites.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -71,7 +71,7 @@ body<=
         return $ret;
     }
 
-    $ret .= LJ::maintainer_linkbar($c, "invites");
+    $ret .= $c->maintainer_linkbar( "invites" );
 
     # Process revoked invitations
     if ($POST{'action:revoke'}) {
@@ -83,7 +83,7 @@ body<=
         foreach (grep { /^revoke_invite/ } keys %POST) {
             push @userids, $POST{$_};
         }
-        if (LJ::revoke_invites($c, @userids)) {
+        if ( $c->revoke_invites( @userids ) ) {
             $ret .= "<div class='warningbar'> " .
                     BML::ml('.invites.cancelled', { num => scalar @userids }) .
                     "</div>";
@@ -101,7 +101,7 @@ body<=
     push @titleattribs, 'A';
 
     # now get sent invites and the users involved
-    my $sent = LJ::get_sent_invites($c) || [];
+    my $sent = $c->get_sent_invites || [];
     my @ids;
     push @ids, ($_->{userid}, $_->{maintid}) foreach @$sent;
     my $us = LJ::load_userids(@ids);
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/community/settings.bml
--- a/htdocs/community/settings.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/community/settings.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -148,8 +148,8 @@ body<=
                 LJ::statushistory_add($cu, $remote, "change_journal_type", $msg);
             }
 
-            LJ::set_comm_settings($cu, $remote, { membership => $qmembership,
-                                   postlevel => $qpostlevel });
+            $cu->set_comm_settings( $remote, { membership => $qmembership,
+                                    postlevel => $qpostlevel } );
 
             # lazy-cleanup: if a community has subscriptions (most likely
             # due to a personal->comm conversion), nuke those subs.
@@ -182,7 +182,7 @@ body<=
             # since journaltype changed
             $cu->invalidate_directory_record;
 
-            $ret .= LJ::maintainer_linkbar($cu, "settings");
+            $ret .= $cu->maintainer_linkbar( "settings" );
             $ret .= "<?h1 $ML{'.success'} h1?>";
             if ($mode eq 'create') {
                 $ret .= "<?p $ML{'.label.commcreated'} p?>";
@@ -270,7 +270,7 @@ body<=
     } else {
         LJ::set_active_crumb('commsettings');
           $ret .= LJ::html_hidden('cuser', $cname);
-          $ret .= LJ::maintainer_linkbar($c, "settings");
+          $ret .= $c->maintainer_linkbar( "settings" );
     }
 
     $ret .= "<div id='left-column'>";
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/manage/invites.bml
--- a/htdocs/manage/invites.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/manage/invites.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -42,7 +42,7 @@ body<=
     my $ret;
 
     # get pending invites
-    my $pending = LJ::get_pending_invites($u) || [];
+    my $pending = $u->get_pending_invites || [];
 
     # short out?
     return "<?h1 $ML{'.none.title'} h1?><?p $ML{'.none.body'} p?>"
@@ -69,10 +69,10 @@ body<=
 
             # now take actions?
             if ($POST{"pending_$commid"} eq 'yes') {
-                my $rval = LJ::accept_comm_invite($u, $us->{$commid});
+                my $rval = $u->accept_comm_invite( $us->{$commid} );
                 push @accepted, [ $commid, $args ] if $rval;
             } elsif ($POST{"pending_$commid"} eq 'no') {
-                my $rval = LJ::reject_comm_invite($u, $us->{$commid});
+                my $rval = $u->reject_comm_invite( $us->{$commid} );
                 push @rejected, $commid if $rval;
             } else {
                 push @undecided, $commid;
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/reject.bml
--- a/htdocs/reject.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/reject.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -43,19 +43,20 @@ body<=
         my $dbh = LJ::get_db_writer();
 
         # get user we're adding
-        my $targetid = $arg->{targetid};
-        return LJ::bad_input($ML{'.error.internerr.invalidaction'}) unless $targetid;
+        my $targetu = LJ::load_userid( $arg->{targetid} );
+        return LJ::bad_input($ML{'.error.internerr.invalidaction'}) unless $targetu;
 
-        return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.rejecting'} p?>"
-            unless LJ::reject_pending_member($aa->{userid}, $targetid);
+        return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.approving'} p?>"
+            unless my $cu = LJ::load_userid( $aa->{userid} );
+        return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.approving'} p?>"
+            unless $cu->reject_pending_member( $targetu );
 
         # return success
-        my $commname = LJ::get_username($aa->{userid});
-        my $username = LJ::get_username($targetid);
+        my $commname = $cu->user;
         return "<?h1 $ML{'.comm.success'} h1?>".
                '<?p ' . BML::ml('.commreject.text', {
-                           user => LJ::ljuser($username),
-                           comm => LJ::ljuser($commname),
+                           user => $targetu->ljuser_display,
+                           comm => $cu->ljuser_display,
                            aopts => "href=\"$LJ::SITEROOT/community/members?authas=$commname\"",
                         }) . ' p?>';
     }
diff -r a7f858ce42d3 -r 4dc0abe1fabe htdocs/tools/endpoints/changerelation.bml
--- a/htdocs/tools/endpoints/changerelation.bml	Tue Aug 31 11:16:42 2010 +0800
+++ b/htdocs/tools/endpoints/changerelation.bml	Tue Aug 31 12:54:57 2010 +0800
@@ -74,12 +74,11 @@ _c?>
         $success = $remote->remove_edge( $targetu, watch => {} );
     } elsif ( $action eq 'join' ) {
         my $error;
-        my $can_join = $remote->can_join( $targetu, errref => \$error ) ? 1 : 0;
-        if ( $can_join ) {
-            $success = LJ::join_community( $remote, $targetu );
+        if ( $remote->can_join( $targetu, errref => \$error ) ) {
+            $success = $remote->join_community( $targetu );
         } else {
             if ( $error eq LJ::Lang::ml( 'edges.join.error.targetnotopen' ) && $targetu->is_moderated_membership ) {
-                LJ::comm_join_request( $targetu, $remote );
+                $targetu->comm_join_request( $remote );
                 $ret{note} = BML::ml( '/community/join.bml.reqsubmitted.body' );
             } else {
                 return $err->( $error );
@@ -94,7 +93,7 @@ _c?>
         $targetu->log_event( 'maintainer_remove', { actiontarget => $remote->id, remote => $remote } )
             if $remote->can_manage( $targetu );
 
-        $success = LJ::leave_community( $remote, $targetu );
+        $success = $remote->leave_community( $targetu );
     } elsif ( $action eq 'setBan' ) {
         my $list_of_banned = LJ::load_rel_user($remote, 'B') || [ ];
 
diff -r a7f858ce42d3 -r 4dc0abe1fabe t/console-community.t
--- a/t/console-community.t	Tue Aug 31 11:16:42 2010 +0800
+++ b/t/console-community.t	Tue Aug 31 12:54:57 2010 +0800
@@ -3,12 +3,12 @@ use Test::More;
 use Test::More;
 use lib "$ENV{LJHOME}/cgi-bin";
 require 'ljlib.pl';
-require 'communitylib.pl';
+use LJ::Community;
 use LJ::Console;
 use LJ::Test qw (temp_user temp_comm);
 local $LJ::T_NO_COMMAND_PRINT = 1;
 
-plan tests => 6;
+plan tests => 8;
 
 my $u = temp_user();
 my $u2 = temp_user();
@@ -34,13 +34,15 @@ is($run->("community " . $comm2->user . 
 is($run->("community " . $comm2->user . " remove " . $u2->user),
    "error: You cannot remove users from this community.");
 
-LJ::join_community($comm, $u2);
+$u2->join_community( $comm );
+ok( $u2->member_of( $comm ), "User is currently member of community." );
 is($run->("community " . $comm->user . " remove " . $u2->user),
    "success: User " . $u2->user . " removed from " . $comm->user);
 ok( ! $u2->member_of( $comm ), "User removed from community." );
 
 # test case where user's removing themselves
-LJ::join_community($comm2, $u);
+$u->join_community( $comm2 );
+ok( $u->member_of( $comm2 ), "User is currently member of community." );
 is($run->("community " . $comm2->user . " remove " . $u->user),
    "success: User " . $u->user . " removed from " . $comm2->user);
 ok( ! $u->member_of( $comm2 ), "User removed self from community." );
diff -r a7f858ce42d3 -r 4dc0abe1fabe t/esn-journalnewentry.t
--- a/t/esn-journalnewentry.t	Tue Aug 31 11:16:42 2010 +0800
+++ b/t/esn-journalnewentry.t	Tue Aug 31 12:54:57 2010 +0800
@@ -5,8 +5,8 @@ use lib "$ENV{LJHOME}/cgi-bin";
 use lib "$ENV{LJHOME}/cgi-bin";
 require 'ljlib.pl';
 require 'ljprotocol.pl';
-require 'communitylib.pl';
 
+use LJ::Community;
 use LJ::Event;
 use LJ::Test qw(memcache_stress temp_user temp_comm);
 use FindBin qw($Bin);
diff -r a7f858ce42d3 -r 4dc0abe1fabe t/wtf.t
--- a/t/wtf.t	Tue Aug 31 11:16:42 2010 +0800
+++ b/t/wtf.t	Tue Aug 31 12:54:57 2010 +0800
@@ -4,7 +4,7 @@ use Test::More;
 use Test::More;
 use lib "$ENV{LJHOME}/cgi-bin";
 require 'ljlib.pl';
-require 'communitylib.pl';
+use LJ::Community;
 use LJ::Test qw (temp_user temp_comm);
 
 plan tests => 67;
--------------------------------------------------------------------------------