#!/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 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: if the user has banned the maintainer, don't accept the invite return LJ::error('comm_user_has_banned') if $u->has_banned($mu); # step 3: 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 commid = ? AND userid = ?", 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 read list, but don't auto-add P edge. my $joined = 0; $joined = $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; # log to userlog if remote is a maintainer $cu->log_event( 'maintainer_remove', { actiontarget => $u->id, remote => $u } ) if $u->can_manage($cu); # 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; } # 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' || ( $crow->{postlevel} eq "select" && $cu->prop('comm_postlevel_new') ); } } 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 => {} ); # also automatically add to default view, as most useful behavior # this only applies if we are doing a quick / easy join, so it won't affect # things if the user is joining + watching communities the long way $u->add_to_default_filters($cu); # 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; # 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); my @settings = qw/membership postlevel/; my $updates = join( ', ', map { $opts->{$_} ? "$_=?" : () } @settings ); my @update_values = map { $opts->{$_} || () } @settings; die "Membership or posting level is not available" unless @update_values; my $cid = $c->userid; my $dbh = LJ::get_db_writer(); $dbh->do( "INSERT INTO community (userid, membership, postlevel) VALUES (?,?,?) ON DUPLICATE KEY UPDATE $updates", undef, $cid, $opts->{membership} || 'open', $opts->{postlevel} || 'members', @update_values ); my $memkey = [ $cid, "commsettings:$cid" ]; LJ::MemCache::delete($memkey); return; } sub maintainer_linkbar { my ( $comm, $page, $in_foundation_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" ? "$manage_link_info{text}" : "$manage_link_info{text}"; } } push @links, ( $page eq "profile" ? "" . LJ::Lang::ml('commlist.actinfo2') . "" : "" . LJ::Lang::ml('commlist.actinfo2') . "", $page eq "customize" ? "" . LJ::Lang::ml('commlist.customize2') . "" : "" . LJ::Lang::ml('commlist.customize2') . "", $page eq "settingsaccount" ? "" . LJ::Lang::ml('commlist.actsettingsaccount') . "" : "" . LJ::Lang::ml('commlist.actsettingsaccount') . "", $page eq "invites" ? "" . LJ::Lang::ml('commlist.actinvites') . "" : "" . LJ::Lang::ml('commlist.actinvites') . "", $page eq "members" ? "" . LJ::Lang::ml('commlist.actmembers2') . "" : "" . LJ::Lang::ml('commlist.actmembers2') . "", $page eq "queue" ? "" . LJ::Lang::ml('commlist.queue') . "" : "" . LJ::Lang::ml('commlist.queue') . "", ); my $ret .= "" . LJ::Lang::ml( 'commlist.managelinks', { user => $comm->ljuser_display } ) . " "; $ret .= join( " | ", @links ); return $in_foundation_page ? $ret : "
$ret
"; } 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; } # returns the membership level of a community sub membership_level { my ($u) = @_; return undef unless $u->is_community; my ($membership_level) = $u->get_comm_settings; return $membership_level || ''; } # returns the post level of a community sub post_level { my $u = $_[0]; return undef unless $u->is_community; my ( $membership_level, $post_level ) = $u->get_comm_settings; return $post_level || ''; } # helper methods for checking some values about communities sub is_closed_membership { $_[0]->membership_level eq 'closed' ? 1 : 0; } sub is_moderated_membership { $_[0]->membership_level eq 'moderated' ? 1 : 0; } sub is_open_membership { $_[0]->membership_level eq 'open' ? 1 : 0; } sub has_moderated_posting { $_[0]->prop('moderated'); } sub has_open_posting { $_[0]->prop('nonmember_posting') ? 1 : 0; } # returns an array of maintainer userids sub maintainer_userids { my ($u) = @_; return () unless $u->is_community; return @{ LJ::load_rel_user_cache( $u->id, 'A' ) }; } # returns an array of moderator userids sub moderator_userids { my ($u) = @_; return () unless $u->is_community && $u->has_moderated_posting; return @{ LJ::load_rel_user_cache( $u->id, 'M' ) }; } # accepts (optionally) a list of roles to filter by # returns two hashrefs: # { userid => { "userid" => userid, "name" => username, "A" => 1, "P" => 1 ... }, ... } # { "M" => count, "P" => count, ... } # # not cached sub get_members_by_role { my ( $cu, $types ) = @_; my @all_types = qw ( A P M N E ); my @filter_by_types = scalar @$types ? @$types : @all_types; my %filter_by = map { $_ => 1 } @filter_by_types; my $type_in = join ", ", map { qq('$_') } @all_types; # need a dbr now my $dbr = LJ::get_db_reader(); # get all community edges my $sth = $dbr->prepare( "SELECT r.targetid, r.type, u.user FROM reluser r, useridmap u " . "WHERE r.targetid = u.userid AND r.userid=? AND r.type IN ( $type_in )" ); $sth->execute( $cu->userid ); my %userinfo; # contains the data that we fetched my %users; # the users we return / are interested in my %count; while ( my ( $id, $type, $user ) = $sth->fetchrow_array ) { $userinfo{$id}->{userid} = $id; $userinfo{$id}->{name} = $user; $userinfo{$id}->{$type} = 1; # filter down to just the roles we are interested in if ( $filter_by{$type} ) { $users{$id} = $userinfo{$id}; # only count if we actually include in the final results $count{$type}++; } } return ( \%users, \%count ); } # accepts a user object to fetch member data of # returns same as get_members_by_role sub get_member { my ( $cu, $u ) = @_; return ( {}, {} ) unless LJ::isu($u); my $dbr = LJ::get_db_reader(); # get all community edges my $results = $dbr->selectall_arrayref( "SELECT r.targetid, r.type, u.user FROM reluser r, useridmap u " . "WHERE r.targetid = u.userid AND r.userid=? AND r.targetid=?", undef, $cu->userid, $u->userid ); my %userinfo; my %count; foreach (@$results) { my ( $id, $type, $user ) = @$_; $userinfo{$id}->{userid} = $id; $userinfo{$id}->{name} = $user; $userinfo{$id}->{$type} = 1; $count{$type}++; } return ( \%userinfo, \%count ); } =head2 C<< $cu->notify_administrator_remove( $admin_u_del, $remote ) >> Notify a user when they've been removed from being admin for a community. $admin_u_del is the user to be removed; $remote is the user doing the removing =cut sub notify_administrator_remove { my ( $cu, $admin_u_del, $remote ) = @_; $cu->_notify_administrator( $admin_u_del, $remote, "maintainer_remove" ); } =head2 C<< $cu->notify_administrator_add( $admin_u_new, $remote ) >> Notify a user when they've been added as an admin to a community. $admin_u_new is the user to be added; $remote is the user doing the adding. =cut sub notify_administrator_add { my ( $cu, $admin_u_new, $remote ) = @_; $cu->_notify_administrator( $admin_u_new, $remote, "maintainer_add" ); } # TODO: check if user wants to receive emails? sub _notify_administrator { my ( $cu, $target_u, $remote, $action ) = @_; $cu->log_event( $action, { actiontarget => $target_u->id, remote => $remote } ); return if !$target_u || $target_u->is_expunged; my $email_body; my $email_subject; my $ml_scope = "/communities/members/edit.tt"; if ( $action eq "maintainer_add" ) { $email_body = LJ::Lang::ml( "$ml_scope.email.admin.add.body", { comm => $cu->name_raw, community_url => $cu->journal_base, community_management_url => "$LJ::SITEROOT/communities/list" } ); $email_subject = LJ::Lang::ml( "$ml_scope.email.admin.add.subject", { comm => $cu->name_raw } ); } else { $email_body = LJ::Lang::ml( "$ml_scope.email.admin.delete.body", { admin => $remote->user, comm => $cu->name_raw, community_url => $cu->journal_base, } ); $email_subject = LJ::Lang::ml( "$ml_scope.email.admin.delete.subject", { comm => $cu->name_raw } ); } LJ::send_formatted_mail( to => $target_u->email_raw, greeting_user => $target_u->user, from => $LJ::BOGUS_EMAIL, fromname => qq{"$LJ::SITENAME"}, subject => $email_subject, body => $email_body, ); } =head2 C<< $cu->get_mod_queue >> Get entries pending moderation =cut sub get_mod_queue { my ($cu) = @_; my $dbcr = LJ::get_cluster_def_reader($cu); my @entries; my $sth = $dbcr->prepare("SELECT * FROM modlog WHERE journalid=?"); $sth->execute( $cu->userid ); while ( $_ = $sth->fetchrow_hashref ) { push @entries, $_; } return sort { $a->{logtime} lt $b->{logtime} } @entries; } 1;