ljr/livejournal/htdocs/community/members.bml

456 lines
17 KiB
Plaintext
Executable File

<?page
title=><?_ml .title _ml?>
body<=
<?_code
{
use strict;
use vars qw(%GET %POST);
LJ::set_active_crumb('commmembers');
return LJ::server_down_html() if $LJ::SERVER_DOWN;
# always have links at top
my $ret = BML::ml('Backlink', {
'link' => '/community/manage.bml',
'text' => $ML{'.manage2'},
});
# get remote
my $remote = LJ::get_remote();
unless ($remote) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'error.noremote'} p?>";
return $ret;
}
my $cname = $GET{'comm'};
return BML::redirect("$LJ::SITEROOT/community/manage.bml") unless $cname;
# get $c object
my $c = LJ::load_user($cname);
unless ($c) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.nocomm'} p?>";
return $ret;
}
my $cid = $c->{'userid'};
# is $remote an admin?
unless (LJ::can_manage_other($remote, $c)) {
$ret .= "<?h1 $ML{'Error'} h1?><?p ";
$ret .= BML::ml('.error.noaccess',
{ 'comm' => LJ::ljuser($cname, { 'type' => 'C' }) });
$ret .= " p?>";
return $ret;
}
my @allattribs = ('member', 'post', 'preapprove', 'moderate', 'admin');
my %attrshort = ( X => 'member', P => 'post', N => 'preapprove', M => 'moderate', A => 'admin');
my %attrshort_r = ( map { $attrshort{$_} => $_ } keys %attrshort ); # reversed
# saving a form submission
if ($POST{'action:update'}) {
# validate form auth
return "<?h1 $ML{'Error'} h1?><?p $ML{'error.invalidform'} p?>"
unless LJ::check_form_auth();
my @userids = split(',', $POST{'ids'});
my @saveattribs = split(',', $POST{'attribs'});
# now we need to create our 'was' list
my %was; # ( userid => { attrib => 1; } )
my %users; # ( userid => username )
foreach my $row (split ';', $POST{was}) { # UID:UNAME:MNPX;UID:UNAME:MX;UID:UNAME:AM
# if this row matches...
if ($row =~ /^(\d+):(\w+):(\w+)$/) {
my ($uid, $name, $attrs) = ($1, $2, $3);
$uid += 0;
next unless $uid && $name && $attrs;
# split attrs and setup
$was{$uid}->{$attrshort{$_}} = 1 foreach split '', $attrs;
$users{$uid} = $name;
}
}
# invite new users
my @to_add;
my @add_errors;
foreach my $num (1..5) {
$POST{"add_$num"} = LJ::trim($POST{"add_$num"});
next unless $POST{"add_$num"} =~ /^\w+$/;
my $target = LJ::load_user($POST{"add_$num"});
unless ($target) {
push @add_errors, BML::ml('.error.nouser',
{ 'user' => $POST{"add_$num"} });
next;
}
unless ($target->{statusvis} eq 'V') {
push @add_errors, BML::ml('.error.notactive',
{ 'user' => LJ::ljuser($POST{"add_$num"}) });
next;
}
my @attr = grep { defined $POST{"add_${num}_$_"} } @saveattribs;
unless (@attr) {
push @add_errors, BML::ml('.error.noattr',
{ 'user' => LJ::ljuser($POST{"add_$num"},
{ 'type' => $target->{'journaltype'} }) });
next;
}
unless ($target->{'journaltype'} eq 'P') {
push @add_errors, BML::ml('.error.invaliduser',
{ 'user' => LJ::ljuser($POST{"add_$num"},
{ 'type' => $target->{'journaltype'} }) });
next;
}
if (grep { $target->{'userid'} == $_ } @userids) {
push @add_errors, BML::ml('.error.alreadyadded',
{ 'user' => LJ::ljuser($POST{"add_$num"},
{ 'type' => $target->{'journaltype'} }) });
next;
}
# insert authactions row
push @to_add, [ $target, \@attr ];
}
return LJ::bad_input(@add_errors) if @add_errors;
# now do the additions if any were needed
my @fail;
my @invited;
if (@to_add) {
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)) {
push @invited, $row;
} else {
push @fail, [ $target, LJ::last_error_code() ];
}
}
}
if (@fail) {
my @bad;
foreach (@fail) {
if ($_->[1] eq 'comm_user_has_banned') {
push @bad, BML::ml('.error.adding', { user => LJ::ljuser($_->[0], { type => 'P' }) });
} elsif ($_->[1] eq 'comm_invite_limit') {
push @bad, BML::ml('.error.limit', { user => LJ::ljuser($_->[0], { type => 'P' }) });
} else {
push @bad, BML::ml('.error.unknown', { user => LJ::ljuser($_->[0], { type => 'P' }) });
}
}
return LJ::bad_input(@bad);
}
# initialize lists of users to update and delete
# keyed on attribute type
my %add = ();
my %delete = ();
foreach (@allattribs) {
$add{$_} = {};
$delete{$_} = {};
}
# need a db handle now
my $dbh = LJ::get_db_writer();
# if we have $other_maints, then there are maintainers not in our
# current view, so they will not be modified, so the user can delete
# all maintainers from the current view
my $in = join(',', map { $dbh->quote($_) } @userids);
my $other_maints = $dbh->selectrow_array("SELECT COUNT(*) FROM reluser " .
"WHERE userid=? AND type='A' " .
"AND targetid NOT IN ($in)",
undef, $cid);
# users already in community
my $maints = 0;
my (%addr, %delr); # store additions and removals sorted by userid
foreach my $id (@userids) {
$id = $id + 0;
my $str;
foreach (@allattribs) {
if ($POST{"edit_${id}_$_"}) {
unless ($was{$id}->{$_}) {
$add{$_}->{$id} = 1;
$addr{$id}->{$_} = 1;
}
} else {
if ($was{$id}->{$_}) {
$delete{$_}->{$id} = 1;
$delr{$id}->{$_} = 1;
}
}
}
$maints++ if $POST{"edit_${id}_admin"};
}
# can't remove ALL maintainers, give them an error so they can
# go back and decide who to keep
if (! $other_maints && $maints < 1) {
$ret .= "<?h1 $ML{'Error'} h1?><?p Your community, " .
LJ::ljuser($cname, { 'type' => 'C' }) .
", must have at least one maintainer. " .
"Please <a href='" . BML::get_uri() . "?comm=$cname'>" .
"go back</a> and add a maintainer. p?>";
return $ret;
}
# delete members
if (%{$delete{'member'}}) {
# TAG:FR:bml_comm_members:del_members
LJ::remove_friend($cid, [ keys %{$delete{'member'}} ]);
}
# log maintainer deletions
foreach my $uid (keys %{$delete{admin} || {}}) {
$c->log_event('maintainer_remove', { actiontarget => $uid, remote => $remote });
}
# delete other rel edges
LJ::clear_rel_multi(
(map { [$cid, $_, 'A'] } keys %{$delete{admin} || {}}),
(map { [$cid, $_, 'P'] } keys %{$delete{post} || {}}),
(map { [$cid, $_, 'M'] } keys %{$delete{moderate} || {}}),
(map { [$cid, $_, 'N'] } keys %{$delete{preapprove} || {}}),
);
# perform additions
my @msgs;
if (%{$add{'member'}}) {
foreach my $id (keys %{$add{'member'}}) {
next if $was{$id}->{'member'};
my $u = LJ::load_userid($id);
if (LJ::u_equals($u, $remote)) {
# you're allowed to add yourself as member
LJ::join_community($remote, $c);
} else {
if (LJ::send_comm_invite($u, $c, $remote, [ 'member' ])) {
# if it succeeded, push the reinvited information
push @msgs, BML::ml('.reinvited2',
{ user => LJ::ljuser($u, { type => 'P' }),
aopts => "href='$LJ::SITEROOT/manage/invites.bml'" });
}
}
}
}
# log maintainer additions
foreach my $uid (keys %{$add{admin} || {}}) {
$c->log_event('maintainer_add', { actiontarget => $uid, remote => $remote });
}
# set rels in db/memcache
LJ::set_rel_multi( (map { [$cid, $_, 'A'] } keys %{$add{admin} || {}}),
(map { [$cid, $_, 'P'] } keys %{$add{post} || {}}),
(map { [$cid, $_, 'M'] } keys %{$add{moderate} || {}}),
(map { [$cid, $_, 'N'] } keys %{$add{preapprove} || {}}),
);
# create some other messages
my %done; # keep track of who we've done
foreach my $id (keys %addr, keys %delr) {
next if $done{$id}++;
my ($str, @astr, @dstr);
push @astr, $ML{"/manage/invites.bml.label.$_"}
foreach keys %{$addr{$id} || {}};
push @dstr, $ML{"/manage/invites.bml.label.$_"}
foreach keys %{$delr{$id} || {}};
$str .= "<li>" . BML::ml('.success.added', { list => join(', ', @astr) }) . "</li>\n" if @astr;
$str .= "<li>" . BML::ml('.success.deleted', { list => join(', ', @dstr) }) . "</li>\n" if @dstr;
push @msgs, LJ::ljuser($users{$id}, { type => 'P' }) . ":<ul>$str</ul>" if $str;
}
$ret .= "<?h1 $ML{'.success.header'} h1?>";
if (@msgs) {
$ret .= "<?p $ML{'.success.message2'} p?>\n<ul>";
$ret .= "<li>$_</li>\n" foreach @msgs;
$ret .= "</ul>";
}
if (@invited) {
$ret .= "<?p ";
$ret .= BML::ml('.success.invited2',
{ aopts => "href='$LJ::SITEROOT/manage/invites.bml'" });
$ret .= " p?><ul>";
foreach my $row (@invited) {
$ret .= "<li>" . LJ::ljuser($row->[0], { type => 'P' }) . ": ";
$ret .= "$ML{\"/manage/invites.bml.label.$_\"}, " foreach @{$row->[1] || []};
chop $ret; chop $ret;
$ret .= "</li>\n";
}
$ret .= "</ul>";
}
$ret .= "<?p $ML{'.success.nochanges'} p?>" unless @msgs || @invited;
$ret .= "<?p " . BML::ml(".success.return", { 'link' => BML::get_uri() . "?comm=$cname" }) . " p?>";
return $ret;
}
# browsing mode
# now get lists of: members, admins, able to post, moderators
my %users = ();
# need a dbr now
my $dbr = LJ::get_db_reader();
# get community members
# TAG:FR:bml_comm_members:get_members
my $sth = $dbr->prepare("SELECT u.userid, u.user FROM useridmap u, friends f " .
"WHERE u.userid=f.friendid AND f.userid=?");
$sth->execute($cid);
while (my ($id, $user) = $sth->fetchrow_array) {
$users{$id}->{'userid'} = $id;
$users{$id}->{'name'} = $user;
$users{$id}->{'member'} = 1;
}
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 ('A','P','M','N')");
$sth->execute($cid);
my %count;
while (my ($id, $type, $user) = $sth->fetchrow_array) {
$users{$id}->{'userid'} = $id;
$users{$id}->{'name'} = $user;
my $key = {'A'=>'admin','P'=>'post','M'=>'moderate','N'=>'preapprove'}->{$type};
$users{$id}->{$key} = 1;
$count{$type}++;
}
# columns of our table, excluding username
my @attribs = ('member', 'post');
LJ::load_user_props($c, 'moderated');
push @attribs, ('preapprove')
if $c->{'moderated'} || $count{'N'};
push @attribs, ('moderate')
if $c->{'moderated'} || $count{'M'};
push @attribs, 'admin';
# sorting method;
my $method = $GET{'sort'};
my $cmp = sub {$a->{'name'} cmp $b->{'name'}};
$cmp = sub {$b->{'member'} <=> $a->{'member'}} if $method eq 'member';
$cmp = sub {$b->{'admin'} <=> $a->{'admin'}} if $method eq 'admin';
$cmp = sub {$b->{'post'} <=> $a->{'post'}} if $method eq 'post';
$cmp = sub {$b->{'moderate'} <=> $a->{'moderate'}} if $method eq 'moderate';
$cmp = sub {$b->{'preapprove'} <=> $a->{'preapprove'}} if $method eq 'preapprove';
my @users = sort $cmp values %users;
my $page_size = 100; # change to adjust page size
# are we going to jump to a specific user ?
my $jumppage;
my $jumpuser;
if (@users > $page_size && $POST{'jumpto'} =~ /^\w+$/) {
my $ct;
foreach (@users) {
$jumppage++ if $ct % $page_size == 0;
if ($POST{'jumpto'} eq $_->{'name'}) {
$jumpuser = $_->{'name'};
last;
}
$ct++;
}
undef $jumppage unless $jumpuser;
}
# how to make links back to this page
my $self_link = sub {
my $sort = "&sort=$GET{'sort'}" if $GET{'sort'};
return "members.bml?comm=$cname&page=$_[0]$sort";
};
my %items = BML::paging(\@users, $jumppage || $GET{'page'}, $page_size);
my $navbar = LJ::paging_bar($items{'page'}, $items{'pages'},
{ 'self_link' => $self_link });
@users = @{$items{'items'}};
# output starts here
$ret .= "<?p " . BML::ml('.name', { 'name' => LJ::ljuser($cname, { 'type' => 'C' }) });
$ret .= " " . BML::ml('.settings', { 'link' => "settings.bml?comm=$cname"}) . " p?>";
$ret .= "<form method='post' action='members.bml?comm=$cname'>";
$ret .= LJ::form_auth();
# jump to user
if ($items{'pages'} > 1) {
$ret .= "<div style='margin-left: 30px;'>Jump to user: ";
$ret .= LJ::html_text({ 'name' => 'jumpto', 'value' => $POST{'jumpto'},
'size' => '10', 'maxlength' => '15' }) . " ";
$ret .= LJ::html_submit(undef, 'Go') . "</div>";
$ret .= $navbar;
}
my $sortlink = BML::get_uri() . "?comm=$cname&sort=";
$ret .= "<br /><div align='center'><table class='borderedtable' cellpadding='2' cellspacing='0'>\n<tr>" .
"<th><a href='${sortlink}name'>$ML{'.key.user'}</a></th>";
$ret .= "<th><a href='${sortlink}$_'>".$ML{".key.$_"}."</a></th>" for (@attribs);
$ret .= "</tr>\n";
# rows for existing users
my $rc = 0;
my @wstrs;
foreach(@users) {
my $rstyle = ($rc++ & 1) ? "<?altcolor1?>" : "<?altcolor2?>";
$ret .= "<tr style='background-color: $rstyle;'><td>" . LJ::ljuser($_->{'name'}) . "</td>";
my $wstr;
foreach my $key (@attribs) {
$ret .= "<td align='center'>";
$ret .= LJ::html_check({ 'name' => "edit_$_->{'userid'}_$key",
'selected' => $_->{$key} });
$wstr .= $attrshort_r{$key} if $_->{$key};
$ret .= "</td>";
}
push @wstrs, "$_->{userid}:$_->{name}:$wstr" if $wstr;
$ret .= "</tr>\n";
}
# if on the last page, let users add to the list
if ($items{'page'} == $items{'pages'}) {
foreach(1..5) {
my $rstyle = ($rc++ & 1) ? "<?altcolor1?>" : "<?altcolor2?>";
$ret .= "<tr style='background-color: $rstyle;'><td>";
$ret .= LJ::html_text({ 'name' => "add_$_", 'size' => '10', 'maxlength' => '15' }) . "</td>";
foreach my $key (@attribs) {
$ret .= "<td align='center'>";
if ($key eq 'member' || $key eq 'post') {
$ret .= LJ::html_check({ name => "add_${_}_$key", selected => 1, });
} else {
$ret .= LJ::html_check({ name => "add_${_}_$key" });
}
$ret .= "</td>";
}
$ret .= "</tr>\n";
}
}
# some hidden values
$ret .= "</table>";
$ret .= LJ::html_hidden('ids', join(',', map { $_->{'userid'}} @users),
'attribs', join(',', @attribs),
'was', join(';', @wstrs)) . "\n";
$ret .= "<p>" . LJ::html_submit('action:update', $ML{'.update'}) . "</p>\n";
$ret .= "</form></div>\n\n";
$ret .= $navbar;
return $ret;
}
_code?>
<=body
page?>